X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Farch%2Fx86_64%2Fkernel%2Fcpuinfo.c;fp=kitten%2Farch%2Fx86_64%2Fkernel%2Fcpuinfo.c;h=f795d92c9d553bb1bf3fa43d2d7aaff6796e98cf;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios-OLD.git diff --git a/kitten/arch/x86_64/kernel/cpuinfo.c b/kitten/arch/x86_64/kernel/cpuinfo.c new file mode 100644 index 0000000..f795d92 --- /dev/null +++ b/kitten/arch/x86_64/kernel/cpuinfo.c @@ -0,0 +1,507 @@ +#include +#include +#include +#include +#include +#include +#include + +/** + * Information about the boot CPU. + * The CPU capabilities stored in this structure are the lowest common + * denominator for all CPUs in the system... in this sense, boot_cpu_data + * is special compared to the corresponding entry in the cpu_info[] array. + */ +struct cpuinfo boot_cpu_data; + +/** + * On AMD multi-core CPUs, the lower bits of the local APIC ID distinquish the + * cores. This function assumes the number of cores is a power of two. + */ +static void __init +amd_detect_cmp(struct cpuinfo *c) +{ + struct arch_cpuinfo *a = &c->arch; + unsigned bits; + unsigned ecx = cpuid_ecx(0x80000008); + + a->x86_pkg_cores = (ecx & 0xff) + 1; + + /* CPU telling us the core id bits shift? */ + bits = (ecx >> 12) & 0xF; + + /* Otherwise recompute */ + if (bits == 0) { + while ((1 << bits) < a->x86_pkg_cores) + bits++; + } + + /* Determine the physical socket ID */ + c->phys_socket_id = c->physical_id >> bits; + + /* Determine the physical core ID (index of core in socket) */ + c->phys_core_id = c->physical_id & ((1 << bits)-1); +} + +static void __init +amd_cpu(struct cpuinfo *c) +{ + unsigned level; + unsigned long value; + unsigned int eax, ebx, ecx, edx; + struct arch_cpuinfo *a = &c->arch; + + /* + * Disable TLB flush filter by setting HWCR.FFDIS on K8 + * bit 6 of msr C001_0015 + * + * Errata 63 for SH-B3 steppings + * Errata 122 for all steppings (F+ have it disabled by default) + */ + if (a->x86_family == 15) { + rdmsrl(MSR_K8_HWCR, value); + value |= 1 << 6; + wrmsrl(MSR_K8_HWCR, value); + } + + /* + * Bit 31 in normal CPUID used for nonstandard 3DNow ID; + * 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway + */ + clear_bit(0*32+31, &a->x86_capability); + + /* On C+ stepping K8 rep microcode works well for copy/memset */ + level = cpuid_eax(1); + if (a->x86_family == 15 && ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)) + set_bit(X86_FEATURE_REP_GOOD, &a->x86_capability); + if (a->x86_family == 0x10) + set_bit(X86_FEATURE_REP_GOOD, &a->x86_capability); + + /* Enable workaround for FXSAVE leak */ + if (a->x86_family >= 6) + set_bit(X86_FEATURE_FXSAVE_LEAK, &a->x86_capability); + + /* Determine L1 Cache and TLB Information */ + if (a->extended_cpuid_level >= 0x80000005) { + cpuid(0x80000005, &eax, &ebx, &ecx, &edx); + + /* 2-MB L1 TLB (inclusive with L2 TLB) */ + a->x86_tlb_size[INST][L1][PAGE_2MB] = (eax & 0xff); + a->x86_tlb_size[DATA][L1][PAGE_2MB] = ((eax >> 16) & 0xff); + + /* 4-KB L1 TLB (inclusive with L2 TLB) */ + a->x86_tlb_size[INST][L1][PAGE_4KB] = (ebx & 0xff); + a->x86_tlb_size[DATA][L1][PAGE_4KB] = ((ebx >> 16) & 0xff); + + /* L1 Instruction Cache */ + a->x86_cache_size[INST][L1] = (edx >> 24); + a->x86_cache_line[INST][L1] = (edx & 0xff); + + /* L1 Data Cache */ + a->x86_cache_size[DATA][L1] = (ecx >> 24); + a->x86_cache_line[DATA][L1] = (ecx & 0xff); + } + + /* Determine L2 Cache and TLB Information */ + if (a->extended_cpuid_level >= 0x80000006) { + cpuid(0x80000006, &eax, &ebx, &ecx, &edx); + + /* 2-MB L2 TLB */ + if ((eax & 0xffff0000) == 0) { + /* Unified I+D 2-MB L2 TLB */ + a->x86_tlb_size[UNIF][L2][PAGE_2MB] = eax & 0xfff; + } else { + a->x86_tlb_size[INST][L2][PAGE_2MB] = eax & 0xfff; + a->x86_tlb_size[DATA][L2][PAGE_2MB] = (eax>>16) & 0xfff; + } + + /* 4-KB L2 TLB */ + if ((ebx & 0xffff0000) == 0) { + /* Unified I+D 4-KB L2 TLB */ + a->x86_tlb_size[UNIF][L2][PAGE_4KB] = ebx & 0xfff; + } else { + a->x86_tlb_size[INST][L2][PAGE_4KB] = ebx & 0xfff; + a->x86_tlb_size[DATA][L2][PAGE_4KB] = (ebx>>16) & 0xfff; + } + + /* Unified L2 Cache */ + a->x86_cache_size[UNIF][L2] = ecx >> 16; + a->x86_cache_line[UNIF][L2] = ecx & 0xff; + } + + /* Determine Advanced Power Management Features */ + if (a->extended_cpuid_level >= 0x80000007) { + a->x86_power = cpuid_edx(0x80000007); + } + + /* Determine Maximum Address Sizes */ + if (a->extended_cpuid_level >= 0x80000008) { + cpuid(0x80000008, &eax, &ebx, &ecx, &edx); + a->x86_virt_bits = (eax >> 8) & 0xff; + a->x86_phys_bits = eax & 0xff; + } + + /* a->x86_power is 8000_0007 edx. Bit 8 is constant TSC */ + if (a->x86_power & (1<<8)) + set_bit(X86_FEATURE_CONSTANT_TSC, &a->x86_capability); + + /* Multi core CPU? */ + if (a->extended_cpuid_level >= 0x80000008) + amd_detect_cmp(c); + + if (a->x86_family == 0xf || a->x86_family == 0x10 || a->x86_family == 0x11) + set_bit(X86_FEATURE_K8, &a->x86_capability); + + /* RDTSC can be speculated around */ + clear_bit(X86_FEATURE_SYNC_RDTSC, &a->x86_capability); + + /* Family 10 doesn't support C states in MWAIT so don't use it */ + if (a->x86_family == 0x10) + clear_bit(X86_FEATURE_MWAIT, &a->x86_capability); +} + +static void __init +intel_cpu(struct cpuinfo *c) +{ + /* TODO */ +} + +/* + * Do some early cpuid on the boot CPU to get some parameter that are + * needed before check_bugs. Everything advanced is in identify_cpu + * below. + */ +void __init +early_identify_cpu(struct cpuinfo *c) +{ + struct arch_cpuinfo *a = &c->arch; + uint32_t tfms; + uint32_t misc; + + /* + * Zero structure, except apic_id should have already been filled in. + */ + uint8_t apic_id = a->apic_id; + memset(a, 0, sizeof(*a)); + a->apic_id = apic_id; + + /* + * Set some defaults to begin with. + */ + a->x86_vendor_id[0] = '\0'; /* Unset */ + a->x86_model_id[0] = '\0'; /* Unset */ + a->x86_clflush_size = 64; + a->x86_pkg_cores = 1; + a->max_cpu_khz = 1000000; /* start out with 1 GHz */ + a->min_cpu_khz = a->max_cpu_khz; + a->cur_cpu_khz = a->max_cpu_khz; + a->tsc_khz = a->max_cpu_khz; + memset(&a->x86_capability, 0, sizeof(a->x86_capability)); + + /* Determine the CPU vendor */ + cpuid(0x00000000, &a->cpuid_level, + (unsigned int *)&a->x86_vendor_id[0], + (unsigned int *)&a->x86_vendor_id[8], + (unsigned int *)&a->x86_vendor_id[4]); + + /* Derive the vendor ID from the vendor string */ + if (!strcmp(a->x86_vendor_id, "AuthenticAMD")) + a->x86_vendor = X86_VENDOR_AMD; + else if (!strcmp(a->x86_vendor_id, "GenuineIntel")) + a->x86_vendor = X86_VENDOR_INTEL; + else + a->x86_vendor = X86_VENDOR_UNKNOWN; + + if (a->cpuid_level == 0) + panic("CPU only has CPUID level 0... is your CPU ancient?"); + + /* + * Determine Intel-defined CPU features and other standard info. + * NOTE: Vendor-specific code may override these later. + */ + cpuid(0x00000001, + &tfms, /* type, family, model, stepping */ + &misc, /* brand, cflush sz, logical cpus, apic id */ + &a->x86_capability[4], /* extended cpu features */ + &a->x86_capability[0] /* cpu features */ + ); + + /* Determine the CPU family */ + a->x86_family = (tfms >> 8) & 0xf; + if (a->x86_family == 0xf) + a->x86_family += ((tfms >> 20) & 0xff); + + /* Determine the CPU model */ + a->x86_model = (tfms >> 4) & 0xf; + if (a->x86_family >= 0x6) + a->x86_model += (((tfms >> 16) & 0xf) << 4); + + /* Determine the CPU stepping */ + a->x86_stepping = tfms & 0xf; + + /* Determine the CLFLUSH size, if the CPU supports CLFLUSH */ + if (a->x86_capability[0] & (1 << X86_FEATURE_CLFLSH)) + a->x86_clflush_size = ((misc >> 8) & 0xff) * 8; + + /* + * Determine the CPU's initial local APIC ID. + * NOTE: The BIOS may change the CPU's Local APIC ID before + * passing control to the OS kernel, however the value + * reported by CPUID will never change. The initial APIC + * ID can sometimes be used to discover CPU topology. + */ + a->initial_lapic_id = (misc >> 24) & 0xff; + + /* TODO: determine page sizes supported via CPUID */ + c->pagesz_mask = (VM_PAGE_4KB | VM_PAGE_2MB); +} + +/* + * This does the hard work of actually picking apart the CPU stuff... + */ +void __init +identify_cpu(void) +{ + int i; + struct cpuinfo *c = &cpu_info[this_cpu]; + struct arch_cpuinfo *a = &c->arch; + + early_identify_cpu(c); + + /* Determine the extended CPUID level */ + a->extended_cpuid_level = cpuid_eax(0x80000000); + + /* Parse extended CPUID information */ + if ((a->extended_cpuid_level & 0xffff0000) == 0x80000000) { + /* Determine AMD-defined CPU features: level 0x80000001 */ + if (a->extended_cpuid_level >= 0x80000001) { + a->x86_capability[1] = cpuid_edx(0x80000001); + a->x86_capability[6] = cpuid_ecx(0x80000001); + } + + /* Determine processor brand/model string */ + if (a->extended_cpuid_level >= 0x80000004) { + unsigned int *v = (unsigned int *) a->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + a->x86_model_id[48] = '\0'; + } else { + strcpy(a->x86_model_id, "Unknown x86-64 Model"); + } + } + + /* + * Vendor-specific initialization. In this section we + * canonicalize the feature flags, meaning if there are + * features a certain CPU supports which CPUID doesn't + * tell us, CPUID claiming incorrect flags, or other bugs, + * we handle them here. + * + * At the end of this section, c->x86_capability better + * indicate the features this CPU genuinely supports! + */ + switch (a->x86_vendor) { + case X86_VENDOR_AMD: + amd_cpu(c); + break; + + case X86_VENDOR_INTEL: + intel_cpu(c); + break; + + case X86_VENDOR_UNKNOWN: + default: + panic("Unknown x86 CPU Vendor."); + } + + /* + * boot_cpu_data holds the common feature set between + * all CPUs; so make sure that we indicate which features are + * common between the CPUs. The first time this routine gets + * executed, c == &boot_cpu_data. + */ + if (c != &boot_cpu_data) { + /* AND the already accumulated flags with these */ + for (i = 0 ; i < NCAPINTS ; i++) + boot_cpu_data.arch.x86_capability[i] &= c->arch.x86_capability[i]; + } +} + +/** + * Prints architecture specific CPU information to the console. + */ +void +print_arch_cpuinfo(struct cpuinfo *c) +{ + int i; + struct arch_cpuinfo *a = &c->arch; + char buf[1024]; + + /* + * These flag bits must match the definitions in . + * NULL means this bit is undefined or reserved; either way it doesn't + * have meaning as far as the kernel is concerned. + */ + static 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", "pbe", + + /* AMD-defined */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "nx", NULL, "mmxext", NULL, + NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", + "3dnowext", "3dnow", + + /* Transmeta-defined */ + "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Other (Linux-defined) */ + "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", + NULL, NULL, NULL, NULL, + "constant_tsc", "up", NULL, "arch_perfmon", + "pebs", "bts", NULL, "sync_rdtsc", + "rep_good", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Intel-defined (#2) */ + "pni", NULL, NULL, "monitor", "ds_cpl", "vmx", "smx", "est", + "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, + NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* VIA/Cyrix/Centaur-defined */ + NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en", + "ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* AMD-defined (#2) */ + "lahf_lm", "cmp_legacy", "svm", "extapic", "cr8_legacy", + "altmovcr8", "abm", "sse4a", + "misalignsse", "3dnowprefetch", + "osvw", "ibs", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Auxiliary (Linux-defined) */ + "ida", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + }; + static char *x86_power_flags[] = { + "ts", /* temperature sensor */ + "fid", /* frequency id control */ + "vid", /* voltage id control */ + "ttp", /* thermal trip */ + "tm", + "stc", + "100mhzsteps", + "hwpstate", + "", /* tsc invariant mapped to constant_tsc */ + /* nothing */ + }; + + printk(KERN_DEBUG " Vendor: %s\n", a->x86_vendor_id); + printk(KERN_DEBUG " Family: %u\n", a->x86_family); + printk(KERN_DEBUG " Model: %u (%s)\n", a->x86_model, a->x86_model_id); + printk(KERN_DEBUG " Stepping: %u\n", a->x86_stepping); + printk(KERN_DEBUG " Frequency: %u.%03u MHz (max=%u.%03u, min=%u.%03u)\n", + a->cur_cpu_khz / 1000, (a->cur_cpu_khz % 1000), + a->max_cpu_khz / 1000, (a->max_cpu_khz % 1000), + a->min_cpu_khz / 1000, (a->min_cpu_khz % 1000)); + + /* L1 Cache Info */ + if (a->x86_cache_size[UNIF][L1] == 0) { + printk(KERN_DEBUG " L1 Cache: I=%u KB, D=%u KB, line size=%u bytes\n", + a->x86_cache_size[INST][L1], + a->x86_cache_size[DATA][L1], + a->x86_cache_line[DATA][L1]); + } else { + printk(KERN_DEBUG " L1 Cache: %u KB (unified I+D), line size=%u bytes\n", + a->x86_cache_size[UNIF][L1], + a->x86_cache_line[UNIF][L1]); + } + + /* L2 Cache Info */ + if (a->x86_cache_size[UNIF][L2] == 0) { + printk(KERN_DEBUG " L2 Cache: I=%u KB, D=%u KB, line size=%u bytes\n", + a->x86_cache_size[INST][L2], + a->x86_cache_size[DATA][L2], + a->x86_cache_line[DATA][L2]); + } else { + printk(KERN_DEBUG " L2 Cache: %u KB (unified I+D), line size=%u bytes\n", + a->x86_cache_size[UNIF][L2], + a->x86_cache_line[UNIF][L2]); + } + + /* 4-KB Page TLB Info */ + printk(KERN_DEBUG " 4-KB TLB: I=%u/%u entries D=%d/%d entries\n", + a->x86_tlb_size[INST][L1][PAGE_4KB], + a->x86_tlb_size[INST][L2][PAGE_4KB], + a->x86_tlb_size[DATA][L1][PAGE_4KB], + a->x86_tlb_size[DATA][L2][PAGE_4KB] + ); + + /* 2-MB Page TLB Info */ + printk(KERN_DEBUG " 2-MB TLB: I=%u/%u entries D=%d/%d entries\n", + a->x86_tlb_size[INST][L1][PAGE_2MB], + a->x86_tlb_size[INST][L2][PAGE_2MB], + a->x86_tlb_size[DATA][L1][PAGE_2MB], + a->x86_tlb_size[DATA][L2][PAGE_2MB] + ); + + /* 1-GB Page TLB Info */ + printk(KERN_DEBUG " 1-GB TLB: I=%u/%u entries D=%d/%d entries\n", + a->x86_tlb_size[INST][L1][PAGE_1GB], + a->x86_tlb_size[INST][L2][PAGE_1GB], + a->x86_tlb_size[DATA][L1][PAGE_1GB], + a->x86_tlb_size[DATA][L2][PAGE_1GB] + ); + + /* Address bits */ + printk(KERN_DEBUG " Address bits: %u bits physical, %u bits virtual\n", + a->x86_phys_bits, + a->x86_virt_bits); + + /* Bytes flushed by CLFLUSH instruction */ + printk(KERN_DEBUG " CLFLUSH size: %u bytes\n", a->x86_clflush_size); + + /* CPU Features */ + buf[0] = '\0'; + for (i = 0; i < 32*NCAPINTS; i++) { + if (cpu_has(c, i) && x86_cap_flags[i] != NULL) { + strcat(buf, x86_cap_flags[i]); + strcat(buf, " "); + } + } + printk(KERN_DEBUG " CPU Features: %s\n", buf); + + /* Power Management Features */ + if (a->x86_power == 0) { + strcpy(buf, "none"); + } else { + buf[0] = '\0'; + for (i = 0; i < 32; i++) { + if ((i < ARRAY_SIZE(x86_power_flags)) && x86_power_flags[i]) { + strcat(buf, x86_power_flags[i]); + strcat(buf, " "); + } else { + char bit_str[7]; + bit_str[0] = '\0'; + sprintf(bit_str, "[%d] ", i); + strcat(buf, bit_str); + } + } + } + printk(KERN_DEBUG " Power Features: %s\n", buf); +} +