added TLB/Cache information

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1592 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Daniel Reinhold 2002-10-22 10:56:17 +00:00
parent 7ab95d9da9
commit 4fea1c8ff7
2 changed files with 302 additions and 41 deletions

View File

@ -0,0 +1,105 @@
// cache-type codes
enum cache_types {
Unused,
Inst_TLB,
Data_TLB,
L1_Inst_Cache,
L1_Data_Cache,
L2_Cache,
L3_Cache,
No_L2_Or_L3,
Trace_Cache
};
// an entry in the TLB/Cache table
typedef struct {
uint8 descriptor;
uint8 cache_type;
char text[70];
} tlbc_info;
//
// the global TLB/Cache table
// technical reference:
// "Intel Processor Identification and the CPUID instruction"
// (ftp://ftp.intel.com/docs/24161821.pdf)
// Page 18
static tlbc_info
Intel_TLB_Cache_Table[] = {
{0x0, 0, ""},
{0x1, Inst_TLB, "4KB pages, 4-way set associative, 32 entries"},
{0x2, Inst_TLB, "4MB pages, fully associative, 2 entries"},
{0x50, Inst_TLB, "4KB, 2MB or 4MB pages, fully associative, 64 entries"},
{0x51, Inst_TLB, "4KB, 2MB or 4MB pages, fully associative, 128 entries"},
{0x52, Inst_TLB, "4KB, 2MB or 4MB pages, fully associative, 256 entries"},
{0x3, Data_TLB, "4KB pages, 4-way set associative, 64 entries"},
{0x4, Data_TLB, "4MB pages, 4-way set associative, 8 entries"},
{0x5b, Data_TLB, "4KB or 4MB pages, fully associative, 64 entries"},
{0x5c, Data_TLB, "4KB or 4MB pages, fully associative, 128 entries"},
{0x5d, Data_TLB, "4KB or 4MB pages, fully associative, 256 entries"},
{0x6, L1_Inst_Cache, "8KB pages, 4-way set associative, 32 byte line size"},
{0x8, L1_Inst_Cache, "16KB pages, 4-way set associative, 32 byte line size"},
{0xa, L1_Data_Cache, "8KB pages, 2-way set associative, 32 byte line size"},
{0xc, L1_Data_Cache, "16KB pages, 4-way set associative, 32 byte line size"},
{0x66, L1_Data_Cache, "8KB pages, 4-way set associative, 64 byte line size"},
{0x67, L1_Data_Cache, "16KB pages, 4-way set associative, 64 byte line size"},
{0x68, L1_Data_Cache, "32KB pages, 4-way set associative, 64 byte line size"},
{0x39, L2_Cache, "128KB pages, 4-way set associative, sectored, 64 byte line size"},
{0x3c, L2_Cache, "256KB pages, 4-way set associative, sectored, 64 byte line size"},
{0x41, L2_Cache, "128KB pages, 4-way set associative, 32 byte line size"},
{0x42, L2_Cache, "256KB pages, 4-way set associative, 32 byte line size"},
{0x43, L2_Cache, "512KB pages, 4-way set associative, 32 byte line size"},
{0x44, L2_Cache, "1MB pages, 4-way set associative, 32 byte line size"},
{0x45, L2_Cache, "2MB pages, 4-way set associative, 32 byte line size"},
{0x79, L2_Cache, "128KB pages, 8-way set associative, sectored, 64 byte line size"},
{0x7a, L2_Cache, "256KB pages, 8-way set associative, sectored, 64 byte line size"},
{0x7b, L2_Cache, "512KB pages, 8-way set associative, sectored, 64 byte line size"},
{0x7c, L2_Cache, "1MB pages, 8-way set associative, sectored, 64 byte line size"},
{0x82, L2_Cache, "256KB pages, 8-way set associative, 32 byte line size"},
{0x83, L2_Cache, "512KB pages, 8-way set associative, 32 byte line size"},
{0x84, L2_Cache, "1MB pages, 8-way set associative, 32 byte line size"},
{0x85, L2_Cache, "2MB pages, 8-way set associative, 32 byte line size"},
{0x40, No_L2_Or_L3, "No 2nd-level cache, or if 2nd-level cache exists, no 3rd-level cache\n"},
{0x22, L3_Cache, "512KB pages, 4-way set associative, sectored, 64 byte line size"},
{0x23, L3_Cache, "1MB pages, 8-way set associative, sectored, 64 byte line size"},
{0x25, L3_Cache, "2MB pages, 8-way set associative, sectored, 64 byte line size"},
{0x29, L3_Cache, "4MB pages, 8-way set associative, sectored, 64 byte line size"},
{0x70, Trace_Cache, "Trace cache: 12K-uops, 8-way set associative\n"},
{0x71, Trace_Cache, "Trace cache: 16K-uops, 8-way set associative\n"},
{0x72, Trace_Cache, "Trace cache: 32K-uops, 8-way set associative\n"},
};
void AMD_identify (cpuid_info *);
void AMD_features (cpuid_info *);
void AMD_TLB_cache (cpuid_info *);
void Cyrix_identify (cpuid_info *);
void Cyrix_features (cpuid_info *);
void Cyrix_TLB_cache (cpuid_info *);
void Intel_identify (cpuid_info *);
void Intel_features (cpuid_info *);
void Intel_TLB_cache (cpuid_info *);
const char *Intel_brand_string (int, int);
void dump_regs (cpuid_info *, const char *, uint32, uint32);
char getoption (char *);
void insert (int *, int, int);
void opt_identify (cpuid_info *, int);
void opt_features (cpuid_info *, int);
void opt_TLB_cache (cpuid_info *, int);
void opt_dump_calls (cpuid_info *, uint32);
void print_regs (cpuid_info *);
void usage (void);

View File

@ -3,29 +3,10 @@
//#include <stdlib.h> -- when exit() is implemented
#include <string.h>
#include "cpuinfo.h"
#include "flag_arrays.c"
void AMD_identify (cpuid_info *);
void AMD_features (cpuid_info *);
void AMD_TLB_cache (cpuid_info *);
void Cyrix_identify (cpuid_info *);
void Cyrix_features (cpuid_info *);
void Cyrix_TLB_cache (cpuid_info *);
void Intel_identify (cpuid_info *);
void Intel_features (cpuid_info *);
void Intel_TLB_cache (cpuid_info *);
const char *Intel_brand_string (int, int);
void dump_regs (cpuid_info *, const char *, uint32, uint32);
char getoption (char *optstr);
void opt_identify (cpuid_info *, int);
void opt_features (cpuid_info *, int);
void opt_TLB_cache (cpuid_info *, int);
void opt_dump_calls (cpuid_info *, uint32);
void print_regs (cpuid_info *);
void usage (void);
static cpuid_info CPU_Data;
@ -60,9 +41,14 @@ getoption(char *optstr)
int
main(int argc, char *argv[])
{
// this program prints out everything you would ever want to know about
// your computer's processor, or your money back, in full (30 days, no hassle)
//
// technical reference:
// "Intel Processor Identification and the CPUID Instruction"
// (ftp://download.intel.com/design/Xeon/applnots/24161821.pdf)
// "Intel Processor Identification and the CPUID Instruction"
// (ftp://download.intel.com/design/Xeon/applnots/24161821.pdf)
const char *no_support = "Sorry, your processor does not support this feature\n";
char option = 0;
int vendor_tag;
@ -79,24 +65,36 @@ main(int argc, char *argv[])
// get initial info (max_level and vendor_tag)
get_cpuid(info, 0, 0);
max_level = info->eax_0.max_eax;
if (max_level < 1) {
printf("\nSorry, your computer does not seem to support the CPUID instruction\n");
return 1;
}
max_level = info->eax_0.max_eax;
vendor_tag = info->regs.ebx;
switch (option) {
case 'i':
opt_identify(info, vendor_tag);
printf("CPU identification:\n\n");
if (max_level < 1)
printf(no_support);
else
opt_identify(info, vendor_tag);
break;
case 'f':
opt_features(info, vendor_tag);
printf("Supported processor features:\n\n");
if (max_level < 1)
printf(no_support);
else
opt_features(info, vendor_tag);
break;
case 't':
opt_TLB_cache(info, vendor_tag);
printf("TLB and cache information:\n\n");
if (max_level < 2)
printf(no_support);
else
opt_TLB_cache(info, vendor_tag);
break;
case 'd':
printf("CPUID instruction call dump:\n\n");
opt_dump_calls(info, max_level);
break;
}
@ -108,13 +106,15 @@ main(int argc, char *argv[])
void
opt_identify(cpuid_info *info, int vendor_tag)
{
// gosh, these CPU manufacturers have really outdone
// themselves on creating cutesy vendorID strings, no?
// (that was sarcasm, btw...)
char vendorID[12+1] = {0};
// the 'vendorid' field of the info struct is not null terminated,
// so it is copied into a properly terminated local string buffer
memcpy(vendorID, info->eax_0.vendorid, 12);
printf("\nCPU identification:\n");
printf("%12s '%s'\n", "Vendor ID:", vendorID);
switch (vendor_tag) {
@ -154,7 +154,7 @@ opt_features(cpuid_info *info, int vendor_tag)
void
opt_TLB_cache(cpuid_info *info, int vendor_tag)
{
{
switch (vendor_tag) {
case 'uneG': // "GenuineIntel"
Intel_TLB_cache(info);
@ -179,6 +179,10 @@ opt_TLB_cache(cpuid_info *info, int vendor_tag)
void
Intel_identify(cpuid_info *info)
{
// the code for this function looks very boring and tedious
// (that's because it's very boring and tedious)
// but I think it's correct anyway (there's always that possibility!)
int type, family, model, sig;
get_cpuid(info, 1, 0);
@ -203,6 +207,11 @@ Intel_identify(cpuid_info *info)
else if (family == 6) printf("Pentium Pro");
else if (family == 15) printf("Pentium 4");
printf("'\n");
if (family == 15) {
int efamily = (info->regs.eax >> 20) & 0xff;
printf("Extended family: %d\n", efamily);
}
printf("%12s %2d '", "Model:", model);
switch (family) {
@ -300,7 +309,8 @@ Intel_brand_string(int id, int processor_signature)
void
Intel_features(cpuid_info *info)
{
//
// for each bit set in the features flag, print out the
// corresponding index in the flags array (too easy!)
int i;
int fflags;
@ -308,7 +318,6 @@ Intel_features(cpuid_info *info)
get_cpuid(info, 1, 0);
fflags = info->eax_1.features;
printf("\nSupported processor features:\n");
for (i = 0; i < 32; ++i)
if (fflags & (1<<i))
printf(" %s\n", Intel_feature_flags[i]);
@ -317,11 +326,150 @@ Intel_features(cpuid_info *info)
}
void
insert (int *a, int elem, int max_index)
{
// inserts a new element into an ordered integer array
// using (what else) the trusty old binary search algorithm...
// assumes the array has sufficient space to do this
// (i.e. it's the caller's problem to check for bounds overflow)
int i;
int lo = 0;
int hi = max_index;
int mid;
// find insert index (will end up in lo)
while (lo < hi) {
mid = (lo + hi) / 2;
if (elem < a[mid])
hi = mid;
else
lo = mid + 1;
}
// bump up the max index (to make room for the new element)
hi = max_index + 1;
// move up all the items after the insert point by one
for (i = hi; i > lo; --i)
a[i] = a[i-1];
// insert the new guy
a[lo] = elem;
}
void
Intel_TLB_cache(cpuid_info *info)
{
//get_cpuid(info, 2, 0);
printf("\nnot just yet...\n");
// displays technical info for the CPU's various instruction and data pipeline caches
// and the TLBs (Translation Lookaside Buffers).
//
// The method for extracting and displaying this info might be less than obvious,
// so here's an explanation:
//
// The TLB/Cache info is stored in a global table. Each entry is uniquely identified
// by a descriptor byte. Additionally, each entry is catagorized according to the
// cache type. A textual string contains the information to display.
//
// To retrieve the CPUs capabilities, the pass loop repeatedly (maybe) calls
// get_cpuid() which fills the registers. Each register is a 4-byte datum,
// capable of holding up to 4 descriptor bytes. The descriptor bytes are extracted
// (thru shifting and masking) and then inserted into a slot array. The slot array
// stores indexes into the table. Since descriptor bytes are not indexes themselves,
// an index array is created and used to convert them. The slot array is filled so
// as to keep the indexes in sorted order -- this guarantees than all entries for
// a particular cache type will display together.
//
// After the pass loop is finished, the slot array contains the indexes of all the
// table entries that apply to the host processor. Displaying the info merely a matter
// of spinning thru the slot array and printing the text. The output is organized by
// the cache types -- this technique *only* works because the TLB/Cache table has been
// carefully arranged in that order.
#define BIT_31_MASK (1 << 31)
int pass, matches; // counters
uint32 reg; // value of an individual register (eax, ebx, ecx, edx)
uint8 db; // descriptor byte
int indexOf[256] = {0}; // converts descriptor bytes to table indexes
int slot[256] = {0}; // indexes into the TLB/Cache table (for entries found)
tlbc_info *tab = Intel_TLB_Cache_Table;
int tabsize = sizeof Intel_TLB_Cache_Table / sizeof(Intel_TLB_Cache_Table[0]);
// fill the index conversion array
int i = 0;
int n = 0;
while (n < tabsize)
indexOf[tab[n++].descriptor] = i++;
// pass loop: insert relevant table indexes into the slots array
i = 0;
for (pass = 0; ; ++pass) {
get_cpuid(info, 2, 0);
// low byte of eax register holds maximum iterations
reg = info->regs.eax;
if (pass >= (reg & 0xff))
break;
reg >>= 8; // skip low byte
while ((db = (reg & 0xff)))
insert(slot, indexOf[db], i++),
reg >>= 8;
reg = info->regs.ebx; // ebx
if ((reg & BIT_31_MASK) == 0)
while ((db = (reg & 0xff)))
insert(slot, indexOf[db], i++),
reg >>= 8;
reg = info->regs.ecx; // ecx
if ((reg & BIT_31_MASK) == 0)
while ((db = (reg & 0xff)))
insert(slot, indexOf[db], i++),
reg >>= 8;
reg = info->regs.edx; // edx
if ((reg & BIT_31_MASK) == 0)
while ((db = (reg & 0xff)))
insert(slot, indexOf[db], i++),
reg >>= 8;
}
// reset slot index for output loop
i = 0;
// eeiuw! an icky macro (but it really does prevent much repetitive code)
#define display_matching_entries(type_name,type_code) \
printf(type_name ":\n"); \
matches = 0; \
while (tab[n = slot[i]].cache_type == type_code) { \
printf(" %s\n", tab[n].text); \
++matches; \
++i; \
} \
if (matches == 0) \
printf(" None\n")
// at long last... output the data
display_matching_entries("Instruction TLB", Inst_TLB);
display_matching_entries("Data TLB", Data_TLB);
display_matching_entries("L1 Instruction Cache", L1_Inst_Cache);
display_matching_entries("L1 Data Cache", L1_Data_Cache);
display_matching_entries("L2 Cache", L2_Cache);
if (tab[slot[i]].cache_type == No_L2_Or_L3)
// no need to print the text on this one, just skip it
++i;
display_matching_entries("L3 Cache", L3_Cache);
display_matching_entries("Trace Cache", Trace_Cache);
}
@ -388,9 +536,10 @@ Cyrix_TLB_cache(cpuid_info *info)
void
opt_dump_calls(cpuid_info *info, uint32 max_level)
{
// dumps the registers for standard (and if supported) extended
// calls to the CPUID instruction
uint32 max_extended;
printf("CPUID instruction call dump:\n");
dump_regs(info, "Standard Calls", 0, max_level);
@ -407,9 +556,11 @@ opt_dump_calls(cpuid_info *info, uint32 max_level)
void
dump_regs(cpuid_info *info, const char *heading, uint32 min_level, uint32 max_level)
{
// gosh, it's so perty (in a text-based output kind of way)
uint32 i;
printf("\n%s (max eax level = %lx)\n", heading, max_level);
printf("%s (max eax level = %lx)\n", heading, max_level);
printf("----------------------------------------------\n");
printf(" Input | Output\n");
printf("----------------------------------------------\n");
@ -424,12 +575,17 @@ dump_regs(cpuid_info *info, const char *heading, uint32 min_level, uint32 max_le
info->regs.ecx,
info->regs.edx);
}
printf("\n");
}
void
print_regs(cpuid_info *info)
{
// this function either prints the contents of all the
// registers as a single character string, or it does
// something else entirely (you decide)
int i;
char s[17] = {0};