]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/i386/kernel/cpu/common.c
[PATCH] i386: Use per-cpu variables for GDT, PDA
[linux-2.6-omap-h63xx.git] / arch / i386 / kernel / cpu / common.c
index 5532fc4e1bf040772c95e74387accb4a5b775993..2335f4464eada9460f41ae1485e5090c16d2d6d1 100644 (file)
 #include <asm/apic.h>
 #include <mach_apic.h>
 #endif
+#include <asm/pda.h>
 
 #include "cpu.h"
 
 DEFINE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
 EXPORT_PER_CPU_SYMBOL(cpu_gdt_descr);
 
+DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
+
+DEFINE_PER_CPU(struct i386_pda, _cpu_pda);
+EXPORT_PER_CPU_SYMBOL(_cpu_pda);
+
 static int cachesize_override __cpuinitdata = -1;
 static int disable_x86_fxsr __cpuinitdata;
 static int disable_x86_serial_nr __cpuinitdata = 1;
@@ -50,7 +56,7 @@ static struct cpu_dev __cpuinitdata default_cpu = {
        .c_init = default_init,
        .c_vendor = "Unknown",
 };
-static struct cpu_dev * this_cpu = &default_cpu;
+static struct cpu_dev * this_cpu __cpuinitdata = &default_cpu;
 
 static int __init cachesize_setup(char *str)
 {
@@ -232,29 +238,14 @@ static int __cpuinit have_cpuid_p(void)
        return flag_is_changeable_p(X86_EFLAGS_ID);
 }
 
-/* Do minimum CPU detection early.
-   Fields really needed: vendor, cpuid_level, family, model, mask, cache alignment.
-   The others are not touched to avoid unwanted side effects.
-
-   WARNING: this function is only called on the BP.  Don't add code here
-   that is supposed to run on all CPUs. */
-static void __init early_cpu_detect(void)
+void __init cpu_detect(struct cpuinfo_x86 *c)
 {
-       struct cpuinfo_x86 *c = &boot_cpu_data;
-
-       c->x86_cache_alignment = 32;
-
-       if (!have_cpuid_p())
-               return;
-
        /* Get vendor name */
        cpuid(0x00000000, &c->cpuid_level,
              (int *)&c->x86_vendor_id[0],
              (int *)&c->x86_vendor_id[8],
              (int *)&c->x86_vendor_id[4]);
 
-       get_cpu_vendor(c, 1);
-
        c->x86 = 4;
        if (c->cpuid_level >= 0x00000001) {
                u32 junk, tfms, cap0, misc;
@@ -271,6 +262,26 @@ static void __init early_cpu_detect(void)
        }
 }
 
+/* Do minimum CPU detection early.
+   Fields really needed: vendor, cpuid_level, family, model, mask, cache alignment.
+   The others are not touched to avoid unwanted side effects.
+
+   WARNING: this function is only called on the BP.  Don't add code here
+   that is supposed to run on all CPUs. */
+static void __init early_cpu_detect(void)
+{
+       struct cpuinfo_x86 *c = &boot_cpu_data;
+
+       c->x86_cache_alignment = 32;
+
+       if (!have_cpuid_p())
+               return;
+
+       cpu_detect(c);
+
+       get_cpu_vendor(c, 1);
+}
+
 static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
 {
        u32 tfms, xlvl;
@@ -305,6 +316,8 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
 #else
                        c->apicid = (ebx >> 24) & 0xFF;
 #endif
+                       if (c->x86_capability[0] & (1<<19))
+                               c->x86_clflush_size = ((ebx >> 8) & 0xff) * 8;
                } else {
                        /* Have CPUID level 0 only - unheard of */
                        c->x86 = 4;
@@ -369,6 +382,7 @@ void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
        c->x86_vendor_id[0] = '\0'; /* Unset */
        c->x86_model_id[0] = '\0';  /* Unset */
        c->x86_max_cores = 1;
+       c->x86_clflush_size = 32;
        memset(&c->x86_capability, 0, sizeof c->x86_capability);
 
        if (!have_cpuid_p()) {
@@ -588,24 +602,73 @@ void __init early_cpu_init(void)
        disable_pse = 1;
 #endif
 }
-/*
- * cpu_init() initializes state that is per-CPU. Some data is already
- * initialized (naturally) in the bootstrap process, such as the GDT
- * and IDT. We reload them nevertheless, this function acts as a
- * 'CPU state barrier', nothing should get across.
- */
-void __cpuinit cpu_init(void)
+
+/* Make sure %gs is initialized properly in idle threads */
+struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
+{
+       memset(regs, 0, sizeof(struct pt_regs));
+       regs->xfs = __KERNEL_PDA;
+       return regs;
+}
+
+/* Initial PDA used by boot CPU */
+struct i386_pda boot_pda = {
+       ._pda = &boot_pda,
+       .cpu_number = 0,
+       .pcurrent = &init_task,
+};
+
+static inline void set_kernel_fs(void)
+{
+       /* Set %fs for this CPU's PDA.  Memory clobber is to create a
+          barrier with respect to any PDA operations, so the compiler
+          doesn't move any before here. */
+       asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_PDA) : "memory");
+}
+
+/* Initialize the CPU's GDT and PDA.  This is either the boot CPU doing itself
+   (still using cpu_gdt_table), or a CPU doing it for a secondary which
+   will soon come up. */
+__cpuinit void init_gdt(int cpu, struct task_struct *idle)
 {
-       int cpu = smp_processor_id();
-       struct tss_struct * t = &per_cpu(init_tss, cpu);
-       struct thread_struct *thread = &current->thread;
-       struct desc_struct *gdt;
        struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
+       struct desc_struct *gdt = per_cpu(cpu_gdt, cpu);
+       struct i386_pda *pda = &per_cpu(_cpu_pda, cpu);
+
+       memcpy(gdt, cpu_gdt_table, GDT_SIZE);
+       cpu_gdt_descr->address = (unsigned long)gdt;
+       cpu_gdt_descr->size = GDT_SIZE - 1;
+
+       pack_descriptor((u32 *)&gdt[GDT_ENTRY_PDA].a,
+                       (u32 *)&gdt[GDT_ENTRY_PDA].b,
+                       (unsigned long)pda, sizeof(*pda) - 1,
+                       0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */
+
+       memset(pda, 0, sizeof(*pda));
+       pda->_pda = pda;
+       pda->cpu_number = cpu;
+       pda->pcurrent = idle;
+}
+
+void __cpuinit cpu_set_gdt(int cpu)
+{
+       struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
+
+       load_gdt(cpu_gdt_descr);
+       set_kernel_fs();
+}
+
+/* Common CPU init for both boot and secondary CPUs */
+static void __cpuinit _cpu_init(int cpu, struct task_struct *curr)
+{
+       struct tss_struct * t = &per_cpu(init_tss, cpu);
+       struct thread_struct *thread = &curr->thread;
 
        if (cpu_test_and_set(cpu, cpu_initialized)) {
                printk(KERN_WARNING "CPU#%d already initialized!\n", cpu);
                for (;;) local_irq_enable();
        }
+
        printk(KERN_INFO "Initializing CPU#%d\n", cpu);
 
        if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
@@ -617,49 +680,16 @@ void __cpuinit cpu_init(void)
                set_in_cr4(X86_CR4_TSD);
        }
 
-       /* The CPU hotplug case */
-       if (cpu_gdt_descr->address) {
-               gdt = (struct desc_struct *)cpu_gdt_descr->address;
-               memset(gdt, 0, PAGE_SIZE);
-               goto old_gdt;
-       }
-       /*
-        * This is a horrible hack to allocate the GDT.  The problem
-        * is that cpu_init() is called really early for the boot CPU
-        * (and hence needs bootmem) but much later for the secondary
-        * CPUs, when bootmem will have gone away
-        */
-       if (NODE_DATA(0)->bdata->node_bootmem_map) {
-               gdt = (struct desc_struct *)alloc_bootmem_pages(PAGE_SIZE);
-               /* alloc_bootmem_pages panics on failure, so no check */
-               memset(gdt, 0, PAGE_SIZE);
-       } else {
-               gdt = (struct desc_struct *)get_zeroed_page(GFP_KERNEL);
-               if (unlikely(!gdt)) {
-                       printk(KERN_CRIT "CPU%d failed to allocate GDT\n", cpu);
-                       for (;;)
-                               local_irq_enable();
-               }
-       }
-old_gdt:
-       /*
-        * Initialize the per-CPU GDT with the boot GDT,
-        * and set up the GDT descriptor:
-        */
-       memcpy(gdt, cpu_gdt_table, GDT_SIZE);
-       cpu_gdt_descr->size = GDT_SIZE - 1;
-       cpu_gdt_descr->address = (unsigned long)gdt;
-
-       load_gdt(cpu_gdt_descr);
        load_idt(&idt_descr);
 
        /*
         * Set up and load the per-CPU TSS and LDT
         */
        atomic_inc(&init_mm.mm_count);
-       current->active_mm = &init_mm;
-       BUG_ON(current->mm);
-       enter_lazy_tlb(&init_mm, current);
+       curr->active_mm = &init_mm;
+       if (curr->mm)
+               BUG();
+       enter_lazy_tlb(&init_mm, curr);
 
        load_esp0(t, thread);
        set_tss_desc(cpu,t);
@@ -671,8 +701,8 @@ old_gdt:
        __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss);
 #endif
 
-       /* Clear %fs and %gs. */
-       asm volatile ("movl %0, %%fs; movl %0, %%gs" : : "r" (0));
+       /* Clear %gs. */
+       asm volatile ("mov %0, %%gs" : : "r" (0));
 
        /* Clear all 6 debug registers: */
        set_debugreg(0, 0);
@@ -690,6 +720,33 @@ old_gdt:
        mxcsr_feature_mask_init();
 }
 
+/* Entrypoint to initialize secondary CPU */
+void __cpuinit secondary_cpu_init(void)
+{
+       int cpu = smp_processor_id();
+       struct task_struct *curr = current;
+
+       _cpu_init(cpu, curr);
+}
+
+/*
+ * cpu_init() initializes state that is per-CPU. Some data is already
+ * initialized (naturally) in the bootstrap process, such as the GDT
+ * and IDT. We reload them nevertheless, this function acts as a
+ * 'CPU state barrier', nothing should get across.
+ */
+void __cpuinit cpu_init(void)
+{
+       int cpu = smp_processor_id();
+       struct task_struct *curr = current;
+
+       /* Set up the real GDT and PDA, so we can transition from the
+          boot_gdt_table & boot_pda. */
+       init_gdt(cpu, curr);
+       cpu_set_gdt(cpu);
+       _cpu_init(cpu, curr);
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
 void __cpuinit cpu_uninit(void)
 {