Files

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;
}