366 lines
15 KiB
C
366 lines
15 KiB
C
#include "cpuid.h"
|
|
|
|
// CPU Cache & TLB info lookup table
|
|
char cpu_cache_tlb_index[] = {
|
|
0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x22, 0x23, 0x25, 0x29,
|
|
0x2C, 0x30, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x50, 0x51, 0x52, 0x5B, 0x5C,
|
|
0x5D, 0x60, 0x66, 0x67, 0x68, 0x70, 0x71, 0x72, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
|
|
0x7D, 0x7F, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0xB0, 0xB3, 0xF0, 0xF1, 0x00
|
|
};
|
|
char cpu_cache_tlb_lookup[][80] = {
|
|
{"Instruction TLB: 4KB Pages, 4-way set associative, 32 entries"},
|
|
{"Instruction TLB: 4MB Pages, 4-way set associative, 2 entries"},
|
|
{"Data TLB: 4KB Pages, 4-way set associative, 64 entries"},
|
|
{"Data TLB: 4MB Pages, 4-way set associative, 8 entries"},
|
|
{"L1 instruction cache: 8KB, 4-way set associative, 32B line size"},
|
|
{"L1 instruction cache: 16KB, 4-way set associative, 32B line size"},
|
|
{"L1 data cache: 8KB, 2-way set associative, 32B line size"},
|
|
{"L1 data cache: 16KB, 4-way set associative, 32B line size"},
|
|
{"L3 cache: 512KB, 4-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L3 cache: 1MB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L3 cache: 2MB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L3 cache: 4MB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L1 data cache: 32KB, 8-way set associative, 64B line size"},
|
|
{"L1 instruction cache: 32KB, 8-way set associative, 64B line size"},
|
|
{"No L2 cache or, if processor contains a valid L2 cache, no L3 cache"},
|
|
{"L2 cache: 128KB, 4-way set associative, 32B line size"},
|
|
{"L2 cache: 256KB, 4-way set associative, 32B line size"},
|
|
{"L2 cache: 512KB, 4-way set associative, 32B line size"},
|
|
{"L2 cache: 1MB, 4-way set associative, 32B line size"},
|
|
{"L2 cache: 2MB, 4-way set associative, 32B line size"},
|
|
{"Instruction TLB: 4KB and 2MB or 4MB pages, 64 entries"},
|
|
{"Instruction TLB: 4KB and 2MB or 4MB pages, 128 entries"},
|
|
{"Instruction TLB: 4KB and 2MB or 4MB pages, 256 entries"},
|
|
{"Data TLB: 4KB and 4MB pages, 64 entries"},
|
|
{"Data TLB: 4KB and 4MB pages, 128 entries"},
|
|
{"Data TLB: 4KB and 4MB pages, 256 entries"},
|
|
{"L1 data cache: 16KB, 8-way set associative, 64B line size"},
|
|
{"L1 data cache: 8KB, 4-way set associative, 64B line size"},
|
|
{"L1 data cache: 16KB, 4-way set associative, 64B line size"},
|
|
{"L1 data cache: 32KB, 4-way set associative, 64B line size"},
|
|
{"Trace cache: 12 K, 8-way set associative"},
|
|
{"Trace cache: 16 K, 8-way set associative"},
|
|
{"Trace cache: 32 K, 8-way set associative"},
|
|
{"L2 cache: 1MB, 4-way set associative, 64B line size"},
|
|
{"L2 cache: 128KB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L2 cache: 256KB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L2 cache: 512KB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L2 cache: 1MB, 8-way set associative, 64B line size, 2 lines per sector"},
|
|
{"L2 cache: 2MB, 8-way set associative, 64B line size"},
|
|
{"L2 cache: 512KB, 2-way set associative, 64B line size"},
|
|
{"L2 cache: 256KB, 8-way set associative, 32B line size"},
|
|
{"L2 cache: 512KB, 8-way set associative, 32B line size"},
|
|
{"L2 cache: 1MB, 8-way set associative, 32B line size"},
|
|
{"L2 cache: 2MB, 8-way set associative, 32B line size"},
|
|
{"L2 cache: 512KB, 4-way set associative, 64B line size"},
|
|
{"L2 cache: 1MB, 8-way set associative, 64B line size"},
|
|
{"Instruction TLB: 4KB Pages, 4-way set associative, 128 entries"},
|
|
{"Data TLB: 4KB Pages, 4-way set associative, 128 entries"},
|
|
{"64-Byte Prefetching"},
|
|
{"128-Byte Prefetching"},
|
|
};
|
|
|
|
// Global information about CPU
|
|
cpu_t cpu_info;
|
|
|
|
/* cpuid_t cpuid(int32_t eax)
|
|
* @input: eax - parameter for CPUID instruction
|
|
* @output: ret val - eax, ebx, ecx, edx generated by CPUID instruction
|
|
* @description: a wrapper for CPUID instruction.
|
|
*/
|
|
cpuid_t cpuid(int32_t eax) {
|
|
cpuid_t ret;
|
|
asm volatile("cpuid"
|
|
: "=a"(ret.eax), "=b"(ret.ebx), "=c"(ret.ecx), "=d"(ret.edx)
|
|
: "a"(eax), "b"(0), "c"(0), "d"(0)
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
/* void cpuid_init()
|
|
* @output: set the cpu_info global variable
|
|
* @description: fetches and formats data of CPU using CPUID instruction,
|
|
* read https://c9x.me/x86/html/file_module_x86_id_45.html for what the codes are about.
|
|
*/
|
|
void cpuid_init() {
|
|
// Handle cpuid 0
|
|
cpuid_t ret = cpuid(0);
|
|
cpu_info.max_id_basic = ret.eax;
|
|
memset(cpu_info.brand, 0, CPUID_BRAND_NAME_LEN + 1);
|
|
int32_t mask;
|
|
int32_t i = 0;
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand[i++] = ((ret.ebx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand[i++] = ((ret.edx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand[i++] = ((ret.ecx >> mask) & 0xff);
|
|
|
|
// Handle cpuid 1
|
|
if(cpu_info.max_id_basic >= 1) {
|
|
ret = cpuid(1);
|
|
cpu_info.version.val = ret.eax;
|
|
cpu_info.topology.val = ret.ebx;
|
|
cpu_info.features_ext.val = ret.ecx;
|
|
cpu_info.features.val = ret.edx;
|
|
} else {
|
|
cpu_info.version.val = 0;
|
|
cpu_info.topology.val = 0;
|
|
cpu_info.features_ext.val = 0;
|
|
cpu_info.features.val = 0;
|
|
}
|
|
|
|
// Handle cpuid 2
|
|
if(cpu_info.max_id_basic >= 2) {
|
|
ret = cpuid(2);
|
|
int8_t max_times = ret.eax & 0xff;
|
|
if(max_times > CPUID_CACHE_MAX_LOOP) max_times = CPUID_CACHE_MAX_LOOP;
|
|
int offset = 0;
|
|
memset(cpu_info.cache, 0, CPUID_CACHE_INFO_LEN);
|
|
for(i = 0; i < max_times; i++) {
|
|
ret = cpuid(2);
|
|
if(0 == (ret.eax & 0x80000000)) {
|
|
for(mask = 8; mask < 32; mask += 8) {
|
|
if(0 == ((ret.eax >> mask) & 0xff)) continue;
|
|
cpu_info.cache[offset++] = ((ret.eax >> mask) & 0xff);
|
|
}
|
|
}
|
|
if(0 == (ret.ebx & 0x80000000)) {
|
|
for(mask = 0; mask < 32; mask += 8) {
|
|
if(0 == ((ret.ebx >> mask) & 0xff)) continue;
|
|
cpu_info.cache[offset++] = ((ret.ebx >> mask) & 0xff);
|
|
}
|
|
}
|
|
if(0 == (ret.ecx & 0x80000000)) {
|
|
for(mask = 0; mask < 32; mask += 8) {
|
|
if(0 == ((ret.ecx >> mask) & 0xff)) continue;
|
|
cpu_info.cache[offset++] = ((ret.ecx >> mask) & 0xff);
|
|
}
|
|
}
|
|
if(0 == (ret.edx & 0x80000000)) {
|
|
for(mask = 0; mask < 32; mask += 8) {
|
|
if(0 == ((ret.edx >> mask) & 0xff)) continue;
|
|
cpu_info.cache[offset++] = ((ret.edx >> mask) & 0xff);
|
|
}
|
|
}
|
|
}
|
|
cpu_info.cache_len = offset;
|
|
|
|
// Do a bubble sort on these attributes
|
|
int i = 0;
|
|
int j = 0;
|
|
for(i = 0; i < offset - 1; i++) {
|
|
for(j = 0; j < offset - 1 - i; j++) {
|
|
if(cpu_info.cache[j] > cpu_info.cache[j + 1]) {
|
|
int8_t t = cpu_info.cache[j];
|
|
cpu_info.cache[j] = cpu_info.cache[j + 1];
|
|
cpu_info.cache[j + 1] = t;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
memset(cpu_info.cache, 0, CPUID_CACHE_INFO_LEN);
|
|
cpu_info.cache_len = 0;
|
|
}
|
|
|
|
// CPUID 3 is for Pentium III only, not implemented
|
|
|
|
// Handle cpuid 4
|
|
if(cpu_info.max_id_basic >= 4) {
|
|
ret = cpuid(4);
|
|
cpu_info.cache_detail.val = ret.eax;
|
|
cpu_info.topology_detail.val = ret.ebx;
|
|
cpu_info.num_of_sets = ret.ecx;
|
|
} else {
|
|
cpu_info.cache_detail.val = 0;
|
|
cpu_info.topology_detail.val = 0;
|
|
cpu_info.num_of_sets = 0;
|
|
}
|
|
|
|
// Handle cpuid 5
|
|
if(cpu_info.max_id_basic >= 5) {
|
|
ret = cpuid(5);
|
|
cpu_info.min_mon_line_size = (uint16_t) ret.eax;
|
|
cpu_info.max_mon_line_size = (uint16_t) ret.ebx;
|
|
} else {
|
|
cpu_info.min_mon_line_size = 0;
|
|
cpu_info.max_mon_line_size = 0;
|
|
}
|
|
|
|
// Handle cpuid 7
|
|
if(cpu_info.max_id_basic >= 7) {
|
|
ret = cpuid(7);
|
|
cpu_info.features_ext2_ebx.val = ret.ebx;
|
|
cpu_info.features_ext2_ecx.val = ret.ecx;
|
|
cpu_info.features_ext2_edx.val = ret.edx;
|
|
} else {
|
|
cpu_info.features_ext2_ebx.val = 0;
|
|
cpu_info.features_ext2_ecx.val = 0;
|
|
cpu_info.features_ext2_edx.val = 0;
|
|
}
|
|
|
|
// Handle cpuid extended 0
|
|
ret = cpuid(0x80000000);
|
|
cpu_info.max_id_extended = ret.eax & 0x7fffffff;
|
|
|
|
// Handle cpuid extended 2-5, processor brand string
|
|
memset(cpu_info.brand_str, 0, CPUID_BRAND_STR_LEN + 1);
|
|
if(cpu_info.max_id_extended >= 4) {
|
|
i = 0;
|
|
ret = cpuid(0x80000002);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.eax >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ebx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ecx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.edx >> mask) & 0xff);
|
|
ret = cpuid(0x80000003);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.eax >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ebx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ecx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.edx >> mask) & 0xff);
|
|
ret = cpuid(0x80000004);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.eax >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ebx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.ecx >> mask) & 0xff);
|
|
for(mask = 0; mask < 32; mask += 8) cpu_info.brand_str[i++] = ((ret.edx >> mask) & 0xff);
|
|
}
|
|
}
|
|
|
|
// Unified FS interface for reading CPUID information
|
|
unified_fs_interface_t cpuid_if = {
|
|
.open = cpuid_open,
|
|
.read = cpuid_read,
|
|
.write = cpuid_write,
|
|
.ioctl = NULL,
|
|
.close = cpuid_close
|
|
};
|
|
|
|
/* int32_t cpuid_open(int32_t* inode, char* filename)
|
|
* @input: all ignored
|
|
* @output: success
|
|
* @description: does nothing.
|
|
*/
|
|
int32_t cpuid_open(int32_t* inode, char* filename) {
|
|
return 0;
|
|
}
|
|
|
|
// Macro for simplifying appending a string to overall buffer.
|
|
#define APPEND_BUFFER(str) \
|
|
memcpy(tmp + pos, str, strlen(str)); \
|
|
pos += strlen(str);
|
|
|
|
// Macro for simplifying checking multiple features and adding them to string.
|
|
#define CHECK_FEATURE(feature, str) \
|
|
if(feature) { \
|
|
memcpy(tmp + pos, str, strlen(str));\
|
|
pos += strlen(str); \
|
|
}
|
|
|
|
/* int32_t cpuid_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len)
|
|
* @input: offset - num of attribute we're outputting
|
|
* buf - buffer to write to
|
|
* len - max number of characters to write into
|
|
* @output: buf - written with CPU information
|
|
* ret val - 0 (SUCCESS) / -1 (FAIL)
|
|
* @description: formats CPU information and writes them into buffer.
|
|
*/
|
|
int32_t cpuid_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len) {
|
|
if(len < CPUID_FS_BUF) return FAIL;
|
|
char tmp[CPUID_FS_BUF];
|
|
char tmp2[CPUID_FS_BUF];
|
|
memset(tmp, 0, CPUID_FS_BUF);
|
|
memset(tmp2, 0, CPUID_FS_BUF);
|
|
int pos = 0;
|
|
switch(*offset) {
|
|
case 0:
|
|
APPEND_BUFFER(cpu_info.brand);
|
|
tmp[pos++] = ' ';
|
|
APPEND_BUFFER(cpu_info.brand_str);
|
|
tmp[pos++] = '\n';
|
|
break;
|
|
case 1:
|
|
APPEND_BUFFER("Family ");
|
|
itoa((cpu_info.version.ext_family_id << 4) | cpu_info.version.family_id, tmp2, 16);
|
|
APPEND_BUFFER(tmp2);
|
|
tmp[pos++] = ',';
|
|
tmp[pos++] = ' ';
|
|
break;
|
|
case 2:
|
|
APPEND_BUFFER("Model ");
|
|
itoa((cpu_info.version.ext_model_id << 4) | cpu_info.version.model_id, tmp2, 16);
|
|
APPEND_BUFFER(tmp2);
|
|
tmp[pos++] = ',';
|
|
tmp[pos++] = ' ';
|
|
break;
|
|
case 3:
|
|
APPEND_BUFFER("Stepping ");
|
|
itoa(cpu_info.version.stepping_id, tmp2, 16);
|
|
APPEND_BUFFER(tmp2);
|
|
tmp[pos++] = '\n';
|
|
break;
|
|
case 4:
|
|
APPEND_BUFFER("Features: ");
|
|
CHECK_FEATURE(cpu_info.features.fpu, "FPU ");
|
|
CHECK_FEATURE(cpu_info.features.pae, "PAE ");
|
|
CHECK_FEATURE(cpu_info.features.acpi, "ACPI ");
|
|
CHECK_FEATURE(cpu_info.features.apic, "APIC ");
|
|
CHECK_FEATURE(cpu_info.features.htt, "HyperThread ");
|
|
CHECK_FEATURE(cpu_info.features_ext.est, "EnhancedSpeedStep ");
|
|
break;
|
|
case 5:
|
|
CHECK_FEATURE(cpu_info.features.mmx, "MMX ");
|
|
CHECK_FEATURE(cpu_info.features.sse, "SSE ");
|
|
CHECK_FEATURE(cpu_info.features.sse2, "SSE2 ");
|
|
CHECK_FEATURE(cpu_info.features_ext.sse3, "SSE3 ");
|
|
CHECK_FEATURE(cpu_info.features_ext.sse41, "SSE4.1 ");
|
|
CHECK_FEATURE(cpu_info.features_ext.sse42, "SSE4.2 ");
|
|
CHECK_FEATURE(cpu_info.features_ext.avx, "AVX ");
|
|
CHECK_FEATURE(cpu_info.features_ext2_ebx.avx2, "AVX2 ");
|
|
CHECK_FEATURE(cpu_info.features_ext2_ebx.avx512f, "AVX512 ");
|
|
break;
|
|
case 6:
|
|
CHECK_FEATURE(cpu_info.features_ext.aes, "AES ");
|
|
CHECK_FEATURE(cpu_info.features_ext.rdrnd, "RDRAND ");
|
|
CHECK_FEATURE(cpu_info.features_ext.vmx, "VMX ");
|
|
CHECK_FEATURE(cpu_info.features_ext2_ebx.sha, "SHA ");
|
|
tmp[pos++] = '\n';
|
|
break;
|
|
case 7:
|
|
APPEND_BUFFER("Cache & TLB Descriptors:");
|
|
break;
|
|
default:
|
|
if(*offset - 8 < cpu_info.cache_len) {
|
|
uint8_t feature_id = cpu_info.cache[*offset - 8];
|
|
uint8_t i = 0;
|
|
while(cpu_cache_tlb_index[i] != feature_id && cpu_cache_tlb_index[i] != 0) i++;
|
|
if(cpu_cache_tlb_index[i] == 0) {
|
|
// Cannot find description in table
|
|
tmp[pos++] = ' ';
|
|
} else {
|
|
APPEND_BUFFER("\n- ");
|
|
APPEND_BUFFER(cpu_cache_tlb_lookup[i]);
|
|
}
|
|
} else if(*offset - 8 == cpu_info.cache_len) {
|
|
tmp[pos++] = '\n';
|
|
}
|
|
}
|
|
(*offset) ++;
|
|
memcpy(buf, tmp, strlen(tmp) + 1);
|
|
return pos;
|
|
}
|
|
|
|
/* int32_t cpuid_write(int32_t* inode, uint32_t* offset, const char* buf, uint32_t len)
|
|
* @input: buf - value of new frequency for RTC
|
|
* len - length of buf, should be sizeof(uint16_t)
|
|
* @output: -1 (FAIL)
|
|
* @description: does nothing, cannot change CPU attribute by software
|
|
*/
|
|
int32_t cpuid_write(int32_t* inode, uint32_t* offset, const char* buf, uint32_t len) {
|
|
return FAIL;
|
|
}
|
|
|
|
/* int32_t cpuid_close(int32_t* inode)
|
|
* @input: inode - ignored
|
|
* @output: 0 (SUCCESS)
|
|
* @description: close CPUID, currently does nothing
|
|
*/
|
|
int32_t cpuid_close(int32_t* inode) {
|
|
return SUCCESS;
|
|
}
|