--- /dev/null
+#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);
+}
+