Skip to content

Commit

Permalink
[PATCH] x86: fix broken SMP boot sequence
Browse files Browse the repository at this point in the history
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: James Bottomley <James.Bottomley@SteelEye.com>
Cc: Zachary Amsden <zach@vmware.com>
Cc: Matt Tolentino <metolent@snoqualmie.dp.intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
James Bottomley authored and Linus Torvalds committed Feb 24, 2006
1 parent 1e275d4 commit 2b932f6
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 21 deletions.
32 changes: 28 additions & 4 deletions arch/i386/kernel/cpu/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -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:
Expand All @@ -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);

/*
Expand Down
12 changes: 7 additions & 5 deletions arch/i386/kernel/efi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 0 additions & 2 deletions arch/i386/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 0 additions & 2 deletions arch/i386/kernel/i386_ksyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 0 additions & 6 deletions arch/i386/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down
6 changes: 4 additions & 2 deletions include/asm-i386/desc.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ struct Xgt_desc_struct {
unsigned short pad;
} __attribute__ ((packed));

extern struct Xgt_desc_struct idt_descr, cpu_gdt_descr[NR_CPUS];
extern struct Xgt_desc_struct idt_descr;
DECLARE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);


static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
{
return ((struct desc_struct *)cpu_gdt_descr[cpu].address);
return (struct desc_struct *)per_cpu(cpu_gdt_descr, cpu).address;
}

#define load_TR_desc() __asm__ __volatile__("ltr %w0"::"q" (GDT_ENTRY_TSS*8))
Expand Down

0 comments on commit 2b932f6

Please sign in to comment.