]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/x86/kernel/cpu/common.c
x86/entry: Remap the TSS into the CPU entry area
[mirror_ubuntu-artful-kernel.git] / arch / x86 / kernel / cpu / common.c
index c8b39870f33e8d5579eb1b30ecd24ace23de9d86..4a38de4c6ede111d2ac87f546037628aa665a982 100644 (file)
@@ -168,6 +168,24 @@ static int __init x86_mpx_setup(char *s)
 }
 __setup("nompx", x86_mpx_setup);
 
+#ifdef CONFIG_X86_64
+static int __init x86_nopcid_setup(char *s)
+{
+       /* nopcid doesn't accept parameters */
+       if (s)
+               return -EINVAL;
+
+       /* do not emit a message if the feature is not present */
+       if (!boot_cpu_has(X86_FEATURE_PCID))
+               return 0;
+
+       setup_clear_cpu_cap(X86_FEATURE_PCID);
+       pr_info("nopcid: PCID feature disabled\n");
+       return 0;
+}
+early_param("nopcid", x86_nopcid_setup);
+#endif
+
 static int __init x86_noinvpcid_setup(char *s)
 {
        /* noinvpcid doesn't accept parameters */
@@ -448,12 +466,28 @@ void load_percpu_segment(int cpu)
        load_stack_canary_segment();
 }
 
-/* Setup the fixmap mapping only once per-processor */
-static inline void setup_fixmap_gdt(int cpu)
+static void set_percpu_fixmap_pages(int fixmap_index, void *ptr,
+                                   int pages, pgprot_t prot)
+{
+       int i;
+
+       for (i = 0; i < pages; i++) {
+               __set_fixmap(fixmap_index - i,
+                            per_cpu_ptr_to_phys(ptr + i * PAGE_SIZE), prot);
+       }
+}
+
+#ifdef CONFIG_X86_32
+/* The 32-bit entry code needs to find cpu_entry_area. */
+DEFINE_PER_CPU(struct cpu_entry_area *, cpu_entry_area);
+#endif
+
+/* Setup the fixmap mappings only once per-processor */
+static inline void setup_cpu_entry_area(int cpu)
 {
 #ifdef CONFIG_X86_64
        /* On 64-bit systems, we use a read-only fixmap GDT. */
-       pgprot_t prot = PAGE_KERNEL_RO;
+       pgprot_t gdt_prot = PAGE_KERNEL_RO;
 #else
        /*
         * On native 32-bit systems, the GDT cannot be read-only because
@@ -464,11 +498,40 @@ static inline void setup_fixmap_gdt(int cpu)
         * On Xen PV, the GDT must be read-only because the hypervisor requires
         * it.
         */
-       pgprot_t prot = boot_cpu_has(X86_FEATURE_XENPV) ?
+       pgprot_t gdt_prot = boot_cpu_has(X86_FEATURE_XENPV) ?
                PAGE_KERNEL_RO : PAGE_KERNEL;
 #endif
 
-       __set_fixmap(get_cpu_gdt_ro_index(cpu), get_cpu_gdt_paddr(cpu), prot);
+       __set_fixmap(get_cpu_entry_area_index(cpu, gdt), get_cpu_gdt_paddr(cpu), gdt_prot);
+
+       /*
+        * The Intel SDM says (Volume 3, 7.2.1):
+        *
+        *  Avoid placing a page boundary in the part of the TSS that the
+        *  processor reads during a task switch (the first 104 bytes). The
+        *  processor may not correctly perform address translations if a
+        *  boundary occurs in this area. During a task switch, the processor
+        *  reads and writes into the first 104 bytes of each TSS (using
+        *  contiguous physical addresses beginning with the physical address
+        *  of the first byte of the TSS). So, after TSS access begins, if
+        *  part of the 104 bytes is not physically contiguous, the processor
+        *  will access incorrect information without generating a page-fault
+        *  exception.
+        *
+        * There are also a lot of errata involving the TSS spanning a page
+        * boundary.  Assert that we're not doing that.
+        */
+       BUILD_BUG_ON((offsetof(struct tss_struct, x86_tss) ^
+                     offsetofend(struct tss_struct, x86_tss)) & PAGE_MASK);
+       BUILD_BUG_ON(sizeof(struct tss_struct) % PAGE_SIZE != 0);
+       set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, tss),
+                               &per_cpu(cpu_tss, cpu),
+                               sizeof(struct tss_struct) / PAGE_SIZE,
+                               PAGE_KERNEL);
+
+#ifdef CONFIG_X86_32
+       this_cpu_write(cpu_entry_area, get_cpu_entry_area(cpu));
+#endif
 }
 
 /* Load the original GDT from the per-cpu structure */
@@ -1210,7 +1273,8 @@ void enable_sep_cpu(void)
        wrmsr(MSR_IA32_SYSENTER_CS, tss->x86_tss.ss1, 0);
 
        wrmsr(MSR_IA32_SYSENTER_ESP,
-             (unsigned long)tss + offsetofend(struct tss_struct, SYSENTER_stack),
+             (unsigned long)&get_cpu_entry_area(cpu)->tss +
+             offsetofend(struct tss_struct, SYSENTER_stack),
              0);
 
        wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long)entry_SYSENTER_32, 0);
@@ -1275,18 +1339,16 @@ void print_cpu_info(struct cpuinfo_x86 *c)
                pr_cont(")\n");
 }
 
-static __init int setup_disablecpuid(char *arg)
+/*
+ * clearcpuid= was already parsed in fpu__init_parse_early_param.
+ * But we need to keep a dummy __setup around otherwise it would
+ * show up as an environment variable for init.
+ */
+static __init int setup_clearcpuid(char *arg)
 {
-       int bit;
-
-       if (get_option(&arg, &bit) && bit >= 0 && bit < NCAPINTS * 32)
-               setup_clear_cpu_cap(bit);
-       else
-               return 0;
-
        return 1;
 }
-__setup("clearcpuid=", setup_disablecpuid);
+__setup("clearcpuid=", setup_clearcpuid);
 
 #ifdef CONFIG_X86_64
 struct desc_ptr idt_descr __ro_after_init = {
@@ -1334,6 +1396,8 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(char, exception_stacks
 /* May not be marked __init: used by software suspend */
 void syscall_init(void)
 {
+       int cpu = smp_processor_id();
+
        wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
        wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
 
@@ -1346,7 +1410,9 @@ void syscall_init(void)
         * AMD doesn't allow SYSENTER in long mode (either 32- or 64-bit).
         */
        wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS);
-       wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL);
+       wrmsrl_safe(MSR_IA32_SYSENTER_ESP,
+                   (unsigned long)&get_cpu_entry_area(cpu)->tss +
+                   offsetofend(struct tss_struct, SYSENTER_stack));
        wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)entry_SYSENTER_compat);
 #else
        wrmsrl(MSR_CSTAR, (unsigned long)ignore_sysret);
@@ -1540,7 +1606,7 @@ void cpu_init(void)
                }
        }
 
-       t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap);
+       t->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET;
 
        /*
         * <= is required because the CPU will access up to
@@ -1554,9 +1620,15 @@ void cpu_init(void)
        BUG_ON(me->mm);
        enter_lazy_tlb(&init_mm, me);
 
-       load_sp0(t, &current->thread);
-       set_tss_desc(cpu, t);
+       setup_cpu_entry_area(cpu);
+
+       /*
+        * Initialize the TSS.  Don't bother initializing sp0, as the initial
+        * task never enters user mode.
+        */
+       set_tss_desc(cpu, &get_cpu_entry_area(cpu)->tss.x86_tss);
        load_TR_desc();
+
        load_mm_ldt(&init_mm);
 
        clear_all_debug_regs();
@@ -1567,7 +1639,6 @@ void cpu_init(void)
        if (is_uv_system())
                uv_cpu_init();
 
-       setup_fixmap_gdt(cpu);
        load_fixmap_gdt(cpu);
 }
 
@@ -1578,7 +1649,6 @@ void cpu_init(void)
        int cpu = smp_processor_id();
        struct task_struct *curr = current;
        struct tss_struct *t = &per_cpu(cpu_tss, cpu);
-       struct thread_struct *thread = &curr->thread;
 
        wait_for_master_cpu(cpu);
 
@@ -1608,12 +1678,18 @@ void cpu_init(void)
        BUG_ON(curr->mm);
        enter_lazy_tlb(&init_mm, curr);
 
-       load_sp0(t, thread);
-       set_tss_desc(cpu, t);
+       setup_cpu_entry_area(cpu);
+
+       /*
+        * Initialize the TSS.  Don't bother initializing sp0, as the initial
+        * task never enters user mode.
+        */
+       set_tss_desc(cpu, &get_cpu_entry_area(cpu)->tss.x86_tss);
        load_TR_desc();
+
        load_mm_ldt(&init_mm);
 
-       t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap);
+       t->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET;
 
 #ifdef CONFIG_DOUBLEFAULT
        /* Set up doublefault TSS pointer in the GDT */
@@ -1625,7 +1701,6 @@ void cpu_init(void)
 
        fpu__init_cpu();
 
-       setup_fixmap_gdt(cpu);
        load_fixmap_gdt(cpu);
 }
 #endif