/* (C) 2002 Marcello Barnaba */ #include #include #include #include #ifdef __linux__ #include #endif #define DEBUG /* * http://osdev.neopages.net/tutorials/gccasmtut.php */ /* * CPUID Documentation * (http://www.intel.com/design/PentiumIII/applnots/24512501.pdf) * * /------------------+------------------------------------\ * | Valore di EAX | Valori ritornati | * +------------------+------------------------------------+ * | 0 | EAX -> Valore massimo riconosciuto | * | | dall`istruzione CPUID | * | +------------------------------------+ * | | EBX:EDX:ECX -> Nome del venditore | * | | del processore | * +------------------+------------------------------------+ * | 1 | EAX -> Modello del processore / | * | | 32bit superiori del numero seriale | * | +------------------------------------+ * | | EDX -> Flag supportati dal | * | | processore | * | +------------------------------------+ * | | EBX:ECX -> Uso riservato da Intel | * +------------------+------------------------------------+ * | 2 | EAX:EBX:ECX:EDX -> Parametri di | * | | configurazione del processore | * +------------------+------------------------------------+ * | 3 | EDX:ECX -> I 64bit inferiori del | * | | numero seriale del processore | * +------------------+------------------------------------+ * * http://www.intel.com/design/Xeon/applnots/24161823.pdf */ typedef void (*cpuid_case_func)(uint32_t, uint32_t, uint32_t, uint32_t); void reg2str(uint32_t *, char *); void cpuid(uint32_t, uint32_t *, uint32_t *, uint32_t *, uint32_t *); void bail(char *); void cpuid_case_0(uint32_t, uint32_t, uint32_t, uint32_t); void cpuid_case_1(uint32_t, uint32_t, uint32_t, uint32_t); void cpuid_case_2(uint32_t, uint32_t, uint32_t, uint32_t); void cpuid_case_3(uint32_t, uint32_t, uint32_t, uint32_t); void print_cpu_information(uint8_t mode, uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx); void get_serial_number(void); void print_caching_model(uint32_t reg, int skip); uint8_t *reg32_to_reg8(uint32_t reg); void reg2str(uint32_t *reg, char *s) { char *p = (char *)reg; uint8_t i = 0; while(i++ < sizeof(uint32_t)) { *s++ = *p++; } } void cpuid(uint32_t mode, uint32_t *_eax, uint32_t *_ebx, uint32_t *_ecx, uint32_t *_edx) { volatile uint32_t eax, ebx, ecx, edx; __asm__ __volatile__ ( " movl %4, %%eax \n" " cpuid \n" : "=a" (eax), "=d" (edx), "=c" (ecx), "=b" (ebx) : "m" (mode) ); *_eax = eax; *_ebx = ebx; *_ecx = ecx; *_edx = edx; return; } void bail(char *me) { printf("Usage: \033[1;31m%s\033[0m [0|1|2|3|a|s|b]\n", me); exit(EXIT_FAILURE); } void print_caching_model(uint32_t reg, int skip) { uint8_t *nibbles; uint8_t i; char *s; char *caching_models[] = { NULL, // 0x0 "Instruction TLB: 4K-byte pages, 4-way set associative, 32 entries", // 0x1 "Instruction TLB: 4M-byte pages, fully associative, 2 entries", // 0x2 "Data TLB: 4K-byte pages, 4-way set associative, 64 entries", // 0x3 "Data TLB: 4M-byte pages, 4-way set associative, 8 entries", // 0x4 NULL, // 0x5 "1st level instruction cache: 8K-bytes, 4-way set associative, 32 byte line size", // 0x6 NULL, // 0x7 "1st level instruction cache: 16K-bytes, 4-way set associative, 32 byte line size", // 0x8 NULL, // 0x9 "1st level data cache: 8K-bytes, 2-way set associative, 32 byte line size", // 0xa NULL, // 0xb "1st level data cache: 16K-bytes, 4-way set associative, 32 byte line size", // 0xc NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0xd - 0x17 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x18 - 0x21 "3rd-level cache: 512K-bytes, 4-way set associative, sectored cache, 64-byte line size",// 0x22 "3rd-level cache: 1M-bytes, 8-way set associative, sectored cache, 64-byte line size", // 0x23 NULL, // 0x24 "3rd-level cache: 2M-bytes, 8-way set associative, sectored cache, 64-byte line size", // 0x25 NULL, NULL, NULL, // 0x26 - 0x28 "3rd-level cache: 4M-bytes, 8-way set associative, sectored cache, 64-byte line size", // 0x29 NULL, NULL, // 0x2a - 0x2b "1st-level data cache: 32K-bytes, 8-way set associative, 64-byte line size", // 0x2c NULL, NULL, NULL, // 0x2d - 0x2f "1st-level instruction cache: 32K-bytes, 8-way set associative, 64-byte line size", // 0x30 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x31 - 0x38 "2nd-level cache: 128K-bytes, 4-way set associative, sectored cache, 64-byte line size",// 0x39 NULL, // 0x3a "2nd-level cache: 128K-bytes, 2-way set associative, sectored cache, 64-byte line size",// 0x3b "2nd-level cache: 256K-bytes, 4-way set associative, sectored cache, 64-byte line size",// 0x3c NULL, NULL, NULL, // 0x3d - 0x3f "No 2nd-level or 3rd-level cache", // 0x40 "2nd-level cache: 128K-bytes, 4-way set associative, 32-byte line size", // 0x41 "2nd-level cache: 256K-bytes, 4-way set associative, 32-byte line size", // 0x42 "2nd-level cache: 512K-bytes, 4-way set associative, 32-byte line size", // 0x43 "2nd-level cache: 1M-bytes, 4-way set associative, 32-byte line size", // 0x44 "2nd-level cache: 2M-bytes, 4-way set associative, 32-byte line size", // 0x45 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x46 - 0x4f "Instruction TLB: 4K, 2M or 4M pages, fully associative, 64 entries", // 0x50 "Instruction TLB: 4K, 2M or 4M pages, fully associative, 128 entries", // 0x51 "Instruction TLB: 4K, 2M or 4M pages, fully associative, 256 entries", // 0x52 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x53 - 0x5a "Data TLB: 4K or 4M pages, fully associative, 64 entries", // 0x5b "Data TLB: 4K or 4M pages, fully associative, 128 entries", // 0x5c "Data TLB: 4K or 4M pages, fully associative, 256 entries", // 0x5d NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x5e - 0x65 "1st-level data cache: 8K-bytes, 4-way set associative, sectored cache, 64-byte line size", // 0x66 "1st-level data cache: 16K-bytes, 4-way set associative, sectored cache, 64-byte line size", // 0x67 "1st-level data cache: 32K-bytes, 4-way set associative, sectored cache, 64-byte line size", // 0x68 NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x69 - 0x6f "Trace cache: 12K-uops, 8-way set associative", // 0x70 "Trace cache: 16K-uops, 8-way set associative", // 0x71 "Trace cache: 32K-uops, 8-way set associative", // 0x72 NULL, NULL, NULL, NULL, NULL, NULL, // 0x73 - 0x78 "2nd-level cache: 128K-bytes, 8-way set associative, sectored cache, 64-byte line size",// 0x79 "2nd-level cache: 256K-bytes, 8-way set associative, sectored cache, 64-byte line size",// 0x7a "2nd-level cache: 512K-bytes, 8-way set associative, sectored cache, 64-byte line size",// 0x7b "2nd-level cache: 1M-bytes, 8-way set associative, sectored cache, 64-byte line size", // 0x7c NULL, NULL, NULL, NULL, NULL, // 0x7d - 0x81 "2nd-level cache: 256K-bytes, 8-way set associative, 32-byte line size", // 0x82 "2nd-level cache: 512K-bytes, 8-way set associative, 32-byte line size", // 0x83 "2nd-level cache: 1M-bytes, 8-way set associative, 32-byte line size", // 0x84 "2nd-level cache: 2M-bytes, 8-way set associative, 32-byte line size", // 0x85 "2nd-level cache: 512K-bytes, 4-way set associative, 64 byte line size", // 0x86 "2nd-level cache: 1M-bytes, 4-way set associative, 64 byte line size", // 0x87 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x88 - 0x8f NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0x90 - 0x9f NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0xa0 - 0xaf "Instruction TLB: 4K-byte pages, 4-ways set associative, 128 entries", // 0xb0 NULL, NULL, // 0xb1 - 0xb2 "Data TLB: 4K-byte pages, 4-way set associative, 128 entries", // 0xb3 }; if((nibbles = reg32_to_reg8(reg))) { for(i = skip; i < 4; i++) { if((s = caching_models[nibbles[i]])) { printf("[%02x] %s\n", nibbles[i], s); } } } return; } uint8_t *reg32_to_reg8(uint32_t reg) { static uint8_t nibbles[4]; if((reg >> 31) == 0) { nibbles[0] = reg & 0xff; nibbles[1] = (reg >> 8) & 0xff; nibbles[2] = (reg >> 16) & 0xff; nibbles[3] = (reg >> 24) & 0xff; return nibbles; } return NULL; } void get_serial_number(void) { uint32_t eax, ebx, ecx, edx; uint16_t nibbles[6]; int supported; cpuid(1, &eax, &ebx, &ecx, &edx); supported = edx & (1 << 18); nibbles[0] = eax >> 16; nibbles[1] = eax & 0x0000ffff; cpuid(3, &eax, &ebx, &ecx, &edx); nibbles[2] = edx >> 16; nibbles[3] = edx & 0x0000ffff; nibbles[4] = ecx >> 16; nibbles[5] = ecx & 0x0000ffff; printf("Processor serial number supported: \033[1m%s\033[0m\n", supported ? "yes" : "NO !"); printf("Processor serial number%s: \033[1m%04X-%04X-%04X-%04X-%04X-%04X\033[0m\n", supported ? "" : " (fake) ", nibbles[0], nibbles[1], nibbles[2], nibbles[3], nibbles[4], nibbles[5]); } void get_brand_string(void) { uint32_t eax, ebx, ecx, edx; cpuid(0x80000000, &eax, &ebx, &ecx, &edx); if(eax > 0x80000000) { char brand_str[48]; cpuid(0x80000002, &eax, &ebx, &ecx, &edx); reg2str(&eax, brand_str); reg2str(&ebx, brand_str + sizeof(uint32_t)); reg2str(&ecx, brand_str + sizeof(uint32_t) * 2); reg2str(&edx, brand_str + sizeof(uint32_t) * 3); cpuid(0x80000003, &eax, &ebx, &ecx, &edx); reg2str(&eax, brand_str + sizeof(uint32_t) * 4); reg2str(&ebx, brand_str + sizeof(uint32_t) * 5); reg2str(&ecx, brand_str + sizeof(uint32_t) * 6); reg2str(&edx, brand_str + sizeof(uint32_t) * 7); cpuid(0x80000004, &eax, &ebx, &ecx, &edx); reg2str(&eax, brand_str + sizeof(uint32_t) * 8); reg2str(&ebx, brand_str + sizeof(uint32_t) * 9); reg2str(&ecx, brand_str + sizeof(uint32_t) * 10); reg2str(&edx, brand_str + sizeof(uint32_t) * 11); printf("Brand String: %s\n", brand_str); } else { printf("Brand String not supported.\n"); } } void cpuid_case_0(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { char vendor[sizeof(uint32_t) * 3 + 1]; memset(vendor, 0x0, sizeof(vendor)); reg2str(&ebx, vendor); reg2str(&edx, vendor + sizeof(uint32_t)); reg2str(&ecx, vendor + sizeof(uint32_t) * 2); printf("Vendor: \033[1m%s\033[0m\n", vendor); return; } void cpuid_case_1(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { int stepping, model, family, type; int brand; int i; char *x86_cap_flags[] = { /* Intel-defined */ "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", NULL }; char *x86_type_flags[] = { "Original OEM", "OverDrive Processor", "Dual Processor", "Reserved" }; char *x86_brands[] = { NULL, "Intel Celeron", "Intel Pentium III", "Intel Pentium III Xeon", "Intel Pentium III", NULL, "Mobile Intel Pentium III", "Mobile Intel Celeron", "Intel Pentium 4", "Intel Pentium 4", "Intel Celeron", "Intel Xeon", "Intel Xeon MP", NULL, "Mobile Intel Pentium 4", "Mobile Intel Celeron" }; int serial_supported; stepping = eax & 0xf; model = (eax >> 4) & 0xf; family = (eax >> 8) & 0xf; type = eax >> 10; printf("Stepping: \033[1m%d\033[0m, model: \033[1m%d\033[0m, family: \033[1m%d\033[0m, type: \033[1m%s\033[0m\n", stepping, model, family, x86_type_flags[type]); printf("Flags: "); for(i = 0; i < sizeof(uint32_t) * 8; i++) { if(edx & (1 << i) && x86_cap_flags[i]) { printf("\033[1m%s\033[0m ", x86_cap_flags[i]); } } printf("\n"); brand = ebx; if(x86_brands[brand]) { printf("Processor Brand: \033[1m%s\033[0m\n", x86_brands[brand]); } serial_supported = edx & (1 << 18); printf("Processor serial number supported: \033[1m%s\033[0m\n", (serial_supported) ? "yes" : "NO !"); if(serial_supported) { printf("Upper 32bits of serial number: \033[1m%04X-%04X\033[0m\n", eax >> 16, eax & 0x0000ffff); } } void cpuid_case_2(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { if((eax & 0xf) == 1) { print_caching_model(eax, 1); print_caching_model(ebx, 0); print_caching_model(ecx, 0); print_caching_model(edx, 0); } } void cpuid_case_3(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { uint16_t nibbles[4]; int serial_supported; nibbles[0] = edx >> 16; nibbles[1] = edx & 0x0000ffff; nibbles[2] = ecx >> 16; nibbles[3] = ecx & 0x0000ffff; cpuid(1, &eax, &ebx, &ecx, &edx); serial_supported = edx & (1 << 18); if(serial_supported) { printf("Lower 64bits of serial number: \033[1m%04X-%04X-%04X-%04X\033[0m\n", nibbles[0], nibbles[1], nibbles[2], nibbles[3]); } else { printf("Serial number is disabled.\n"); } } void print_cpu_information(uint8_t mode, uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { cpuid_case_func case_functions[] = { cpuid_case_0, cpuid_case_1, cpuid_case_2, cpuid_case_3 }; #ifdef DEBUG printf("[%d] EAX: \033[1m%x\033[0m, EBX: \033[1m%x\033[0m, ECX: \033[1m%x\033[0m, EDX: \033[1m%x\033[0m\n", mode, eax, ebx, ecx, edx); #endif printf("\n"); (*(case_functions[mode]))(eax, ebx, ecx, edx); } int main(int argc, char **argv) { uint32_t eax, ebx, ecx, edx; int mode; char *me = argv[0]; if(argc < 2) { bail(me); } switch(*argv[1]) { case 'a': { int i = -1; while(++i < 4) { cpuid(i, &eax, &ebx, &ecx, &edx); print_cpu_information(i, eax, ebx, ecx, edx); } } break; case 's': get_serial_number(); break; case 'b': get_brand_string(); break; default: mode = *argv[1] - 0x30; if(mode < 0 || mode > 3) { bail(me); } cpuid(mode, &eax, &ebx, &ecx, &edx); print_cpu_information(mode, eax, ebx, ecx, edx); } return EXIT_SUCCESS; }