Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / arch / x86_64 / kernel / cpuinfo.c
diff --git a/kitten/arch/x86_64/kernel/cpuinfo.c b/kitten/arch/x86_64/kernel/cpuinfo.c
new file mode 100644 (file)
index 0000000..f795d92
--- /dev/null
@@ -0,0 +1,507 @@
+#include <lwk/kernel.h>
+#include <lwk/init.h>
+#include <lwk/smp.h>
+#include <lwk/cpuinfo.h>
+#include <lwk/aspace.h>
+#include <arch/processor.h>
+#include <arch/proto.h>
+
+/**
+ * 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 <arch/cpufeature.h>.
+        * 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);
+}
+