Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b932f6c authored by James Bottomley's avatar James Bottomley Committed by Linus Torvalds
Browse files

[PATCH] x86: fix broken SMP boot sequence



Recent GDT changes broke the SMP boot sequence if the booting CPU is
numbered anything other than zero.  There's also a subtle source of error
in that the boot time CPU now uses cpu_gdt_table (which is actually the GDT
for booting CPUs in head.S).  This patch fixes both problems by making GDT
descriptors themselves allocated from a per_cpu area and switching to them
in cpu_init(), which now means that cpu_gdt_table is exclusively used for
booting CPUs again.

Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
Cc: Zachary Amsden <zach@vmware.com>
Cc: Matt Tolentino <metolent@snoqualmie.dp.intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1e275d40
Loading
Loading
Loading
Loading
+28 −4
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <linux/smp.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/bootmem.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
#include <asm/i387.h>
@@ -18,6 +19,9 @@

#include "cpu.h"

DEFINE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
EXPORT_PER_CPU_SYMBOL(cpu_gdt_descr);

DEFINE_PER_CPU(unsigned char, cpu_16bit_stack[CPU_16BIT_STACK_SIZE]);
EXPORT_PER_CPU_SYMBOL(cpu_16bit_stack);

@@ -571,8 +575,9 @@ void __devinit cpu_init(void)
	int cpu = smp_processor_id();
	struct tss_struct * t = &per_cpu(init_tss, cpu);
	struct thread_struct *thread = &current->thread;
	struct desc_struct *gdt = get_cpu_gdt_table(cpu);
	struct desc_struct *gdt;
	__u32 stk16_off = (__u32)&per_cpu(cpu_16bit_stack, cpu);
	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);

	if (cpu_test_and_set(cpu, cpu_initialized)) {
		printk(KERN_WARNING "CPU#%d already initialized!\n", cpu);
@@ -589,6 +594,25 @@ void __devinit cpu_init(void)
		set_in_cr4(X86_CR4_TSD);
	}

	/*
	 * 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();
		}
	}

	/*
	 * Initialize the per-CPU GDT with the boot GDT,
	 * and set up the GDT descriptor:
@@ -601,10 +625,10 @@ void __devinit cpu_init(void)
		((((__u64)stk16_off) << 32) & 0xff00000000000000ULL) |
		(CPU_16BIT_STACK_SIZE - 1);

	cpu_gdt_descr[cpu].size = GDT_SIZE - 1;
 	cpu_gdt_descr[cpu].address = (unsigned long)gdt;
	cpu_gdt_descr->size = GDT_SIZE - 1;
 	cpu_gdt_descr->address = (unsigned long)gdt;

	load_gdt(&cpu_gdt_descr[cpu]);
	load_gdt(cpu_gdt_descr);
	load_idt(&idt_descr);

	/*
+7 −5
Original line number Diff line number Diff line
@@ -103,17 +103,19 @@ static void efi_call_phys_prelog(void)
	 */
	local_flush_tlb();

	cpu_gdt_descr[0].address = __pa(cpu_gdt_descr[0].address);
	load_gdt((struct Xgt_desc_struct *) __pa(&cpu_gdt_descr[0]));
	per_cpu(cpu_gdt_descr, 0).address =
				 __pa(per_cpu(cpu_gdt_descr, 0).address);
	load_gdt((struct Xgt_desc_struct *)__pa(&per_cpu(cpu_gdt_descr, 0)));
}

static void efi_call_phys_epilog(void)
{
	unsigned long cr4;

	cpu_gdt_descr[0].address =
		(unsigned long) __va(cpu_gdt_descr[0].address);
	load_gdt(&cpu_gdt_descr[0]);
	per_cpu(cpu_gdt_descr, 0).address =
			(unsigned long)__va(per_cpu(cpu_gdt_descr, 0).address);
	load_gdt((struct Xgt_desc_struct *)__va(&per_cpu(cpu_gdt_descr, 0)));

	cr4 = read_cr4();

	if (cr4 & X86_CR4_PSE) {
+0 −2
Original line number Diff line number Diff line
@@ -534,5 +534,3 @@ ENTRY(cpu_gdt_table)
	.quad 0x0000000000000000	/* 0xf0 - unused */
	.quad 0x0000000000000000	/* 0xf8 - GDT entry 31: double-fault TSS */
	/* Be sure this is zeroed to avoid false validations in Xen */
	.fill PAGE_SIZE_asm / 8 - GDT_ENTRIES,8,0
+0 −2
Original line number Diff line number Diff line
@@ -3,8 +3,6 @@
#include <asm/checksum.h>
#include <asm/desc.h>

EXPORT_SYMBOL_GPL(cpu_gdt_descr);

EXPORT_SYMBOL(__down_failed);
EXPORT_SYMBOL(__down_failed_interruptible);
EXPORT_SYMBOL(__down_failed_trylock);
+0 −6
Original line number Diff line number Diff line
@@ -898,12 +898,6 @@ static int __devinit do_boot_cpu(int apicid, int cpu)
	unsigned long start_eip;
	unsigned short nmi_high = 0, nmi_low = 0;

	if (!cpu_gdt_descr[cpu].address &&
	    !(cpu_gdt_descr[cpu].address = get_zeroed_page(GFP_KERNEL))) {
		printk("Failed to allocate GDT for CPU %d\n", cpu);
		return 1;
	}

	++cpucount;

	/*
Loading