diff options
Diffstat (limited to 'arch/x86/kernel/head_64.S')
-rw-r--r-- | arch/x86/kernel/head_64.S | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 0e275d495563..c8ace880661b 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <asm/msr.h> | 19 | #include <asm/msr.h> |
20 | #include <asm/cache.h> | 20 | #include <asm/cache.h> |
21 | #include <asm/processor-flags.h> | 21 | #include <asm/processor-flags.h> |
22 | #include <asm/percpu.h> | ||
22 | 23 | ||
23 | #ifdef CONFIG_PARAVIRT | 24 | #ifdef CONFIG_PARAVIRT |
24 | #include <asm/asm-offsets.h> | 25 | #include <asm/asm-offsets.h> |
@@ -204,6 +205,23 @@ ENTRY(secondary_startup_64) | |||
204 | pushq $0 | 205 | pushq $0 |
205 | popfq | 206 | popfq |
206 | 207 | ||
208 | #ifdef CONFIG_SMP | ||
209 | /* | ||
210 | * early_gdt_base should point to the gdt_page in static percpu init | ||
211 | * data area. Computing this requires two symbols - __per_cpu_load | ||
212 | * and per_cpu__gdt_page. As linker can't do no such relocation, do | ||
213 | * it by hand. As early_gdt_descr is manipulated by C code for | ||
214 | * secondary CPUs, this should be done only once for the boot CPU | ||
215 | * when early_gdt_descr_base contains zero. | ||
216 | */ | ||
217 | movq early_gdt_descr_base(%rip), %rax | ||
218 | testq %rax, %rax | ||
219 | jnz 1f | ||
220 | movq $__per_cpu_load, %rax | ||
221 | addq $per_cpu__gdt_page, %rax | ||
222 | movq %rax, early_gdt_descr_base(%rip) | ||
223 | 1: | ||
224 | #endif | ||
207 | /* | 225 | /* |
208 | * We must switch to a new descriptor in kernel space for the GDT | 226 | * We must switch to a new descriptor in kernel space for the GDT |
209 | * because soon the kernel won't have access anymore to the userspace | 227 | * because soon the kernel won't have access anymore to the userspace |
@@ -226,12 +244,18 @@ ENTRY(secondary_startup_64) | |||
226 | movl %eax,%fs | 244 | movl %eax,%fs |
227 | movl %eax,%gs | 245 | movl %eax,%gs |
228 | 246 | ||
229 | /* | 247 | /* Set up %gs. |
230 | * Setup up a dummy PDA. this is just for some early bootup code | 248 | * |
231 | * that does in_interrupt() | 249 | * On SMP, %gs should point to the per-cpu area. For initial |
232 | */ | 250 | * boot, make %gs point to the init data section. For a |
251 | * secondary CPU,initial_gs should be set to its pda address | ||
252 | * before the CPU runs this code. | ||
253 | * | ||
254 | * On UP, initial_gs points to PER_CPU_VAR(__pda) and doesn't | ||
255 | * change. | ||
256 | */ | ||
233 | movl $MSR_GS_BASE,%ecx | 257 | movl $MSR_GS_BASE,%ecx |
234 | movq $empty_zero_page,%rax | 258 | movq initial_gs(%rip),%rax |
235 | movq %rax,%rdx | 259 | movq %rax,%rdx |
236 | shrq $32,%rdx | 260 | shrq $32,%rdx |
237 | wrmsr | 261 | wrmsr |
@@ -257,6 +281,12 @@ ENTRY(secondary_startup_64) | |||
257 | .align 8 | 281 | .align 8 |
258 | ENTRY(initial_code) | 282 | ENTRY(initial_code) |
259 | .quad x86_64_start_kernel | 283 | .quad x86_64_start_kernel |
284 | ENTRY(initial_gs) | ||
285 | #ifdef CONFIG_SMP | ||
286 | .quad __per_cpu_load | ||
287 | #else | ||
288 | .quad PER_CPU_VAR(__pda) | ||
289 | #endif | ||
260 | __FINITDATA | 290 | __FINITDATA |
261 | 291 | ||
262 | ENTRY(stack_start) | 292 | ENTRY(stack_start) |
@@ -401,7 +431,12 @@ NEXT_PAGE(level2_spare_pgt) | |||
401 | .globl early_gdt_descr | 431 | .globl early_gdt_descr |
402 | early_gdt_descr: | 432 | early_gdt_descr: |
403 | .word GDT_ENTRIES*8-1 | 433 | .word GDT_ENTRIES*8-1 |
404 | .quad per_cpu__gdt_page | 434 | #ifdef CONFIG_SMP |
435 | early_gdt_descr_base: | ||
436 | .quad 0x0000000000000000 | ||
437 | #else | ||
438 | .quad per_cpu__gdt_page | ||
439 | #endif | ||
405 | 440 | ||
406 | ENTRY(phys_base) | 441 | ENTRY(phys_base) |
407 | /* This must match the first entry in level2_kernel_pgt */ | 442 | /* This must match the first entry in level2_kernel_pgt */ |