diff options
author | David S. Miller <davem@davemloft.net> | 2014-10-23 15:58:13 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-24 12:52:49 -0400 |
commit | ef3e035c3a9b81da8a778bc333d10637acf6c199 (patch) | |
tree | 4659112780d9a0630c5ffd7167405e2c916d9707 | |
parent | 61ed53deb1c6a4386d8710dbbfcee8779c381931 (diff) |
sparc64: Fix register corruption in top-most kernel stack frame during boot.
Meelis Roos reported that kernels built with gcc-4.9 do not boot, we
eventually narrowed this down to only impacting machines using
UltraSPARC-III and derivitive cpus.
The crash happens right when the first user process is spawned:
[ 54.451346] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004
[ 54.451346]
[ 54.571516] CPU: 1 PID: 1 Comm: init Not tainted 3.16.0-rc2-00211-gd7933ab #96
[ 54.666431] Call Trace:
[ 54.698453] [0000000000762f8c] panic+0xb0/0x224
[ 54.759071] [000000000045cf68] do_exit+0x948/0x960
[ 54.823123] [000000000042cbc0] fault_in_user_windows+0xe0/0x100
[ 54.902036] [0000000000404ad0] __handle_user_windows+0x0/0x10
[ 54.978662] Press Stop-A (L1-A) to return to the boot prom
[ 55.050713] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004
Further investigation showed that compiling only per_cpu_patch() with
an older compiler fixes the boot.
Detailed analysis showed that the function is not being miscompiled by
gcc-4.9, but it is using a different register allocation ordering.
With the gcc-4.9 compiled function, something during the code patching
causes some of the %i* input registers to get corrupted. Perhaps
we have a TLB miss path into the firmware that is deep enough to
cause a register window spill and subsequent restore when we get
back from the TLB miss trap.
Let's plug this up by doing two things:
1) Stop using the firmware stack for client interface calls into
the firmware. Just use the kernel's stack.
2) As soon as we can, call into a new function "start_early_boot()"
to put a one-register-window buffer between the firmware's
deepest stack frame and the top-most initial kernel one.
Reported-by: Meelis Roos <mroos@linux.ee>
Tested-by: Meelis Roos <mroos@linux.ee>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc/include/asm/oplib_64.h | 3 | ||||
-rw-r--r-- | arch/sparc/include/asm/setup.h | 2 | ||||
-rw-r--r-- | arch/sparc/kernel/entry.h | 3 | ||||
-rw-r--r-- | arch/sparc/kernel/head_64.S | 40 | ||||
-rw-r--r-- | arch/sparc/kernel/hvtramp.S | 1 | ||||
-rw-r--r-- | arch/sparc/kernel/setup_64.c | 28 | ||||
-rw-r--r-- | arch/sparc/kernel/trampoline_64.S | 12 | ||||
-rw-r--r-- | arch/sparc/prom/cif.S | 5 | ||||
-rw-r--r-- | arch/sparc/prom/init_64.c | 6 | ||||
-rw-r--r-- | arch/sparc/prom/p1275.c | 2 |
10 files changed, 40 insertions, 62 deletions
diff --git a/arch/sparc/include/asm/oplib_64.h b/arch/sparc/include/asm/oplib_64.h index f34682430fcf..2e3a4add8591 100644 --- a/arch/sparc/include/asm/oplib_64.h +++ b/arch/sparc/include/asm/oplib_64.h | |||
@@ -62,7 +62,8 @@ struct linux_mem_p1275 { | |||
62 | /* You must call prom_init() before using any of the library services, | 62 | /* You must call prom_init() before using any of the library services, |
63 | * preferably as early as possible. Pass it the romvec pointer. | 63 | * preferably as early as possible. Pass it the romvec pointer. |
64 | */ | 64 | */ |
65 | void prom_init(void *cif_handler, void *cif_stack); | 65 | void prom_init(void *cif_handler); |
66 | void prom_init_report(void); | ||
66 | 67 | ||
67 | /* Boot argument acquisition, returns the boot command line string. */ | 68 | /* Boot argument acquisition, returns the boot command line string. */ |
68 | char *prom_getbootargs(void); | 69 | char *prom_getbootargs(void); |
diff --git a/arch/sparc/include/asm/setup.h b/arch/sparc/include/asm/setup.h index f5fffd84d0dd..29d64b1758ed 100644 --- a/arch/sparc/include/asm/setup.h +++ b/arch/sparc/include/asm/setup.h | |||
@@ -48,6 +48,8 @@ unsigned long safe_compute_effective_address(struct pt_regs *, unsigned int); | |||
48 | #endif | 48 | #endif |
49 | 49 | ||
50 | #ifdef CONFIG_SPARC64 | 50 | #ifdef CONFIG_SPARC64 |
51 | void __init start_early_boot(void); | ||
52 | |||
51 | /* unaligned_64.c */ | 53 | /* unaligned_64.c */ |
52 | int handle_ldf_stq(u32 insn, struct pt_regs *regs); | 54 | int handle_ldf_stq(u32 insn, struct pt_regs *regs); |
53 | void handle_ld_nf(u32 insn, struct pt_regs *regs); | 55 | void handle_ld_nf(u32 insn, struct pt_regs *regs); |
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h index ebaba6167dd4..88d322b67fac 100644 --- a/arch/sparc/kernel/entry.h +++ b/arch/sparc/kernel/entry.h | |||
@@ -65,13 +65,10 @@ struct pause_patch_entry { | |||
65 | extern struct pause_patch_entry __pause_3insn_patch, | 65 | extern struct pause_patch_entry __pause_3insn_patch, |
66 | __pause_3insn_patch_end; | 66 | __pause_3insn_patch_end; |
67 | 67 | ||
68 | void __init per_cpu_patch(void); | ||
69 | void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *, | 68 | void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *, |
70 | struct sun4v_1insn_patch_entry *); | 69 | struct sun4v_1insn_patch_entry *); |
71 | void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *, | 70 | void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *, |
72 | struct sun4v_2insn_patch_entry *); | 71 | struct sun4v_2insn_patch_entry *); |
73 | void __init sun4v_patch(void); | ||
74 | void __init boot_cpu_id_too_large(int cpu); | ||
75 | extern unsigned int dcache_parity_tl1_occurred; | 72 | extern unsigned int dcache_parity_tl1_occurred; |
76 | extern unsigned int icache_parity_tl1_occurred; | 73 | extern unsigned int icache_parity_tl1_occurred; |
77 | 74 | ||
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index 4fdeb8040d4d..3d61fcae7ee3 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S | |||
@@ -672,14 +672,12 @@ tlb_fixup_done: | |||
672 | sethi %hi(init_thread_union), %g6 | 672 | sethi %hi(init_thread_union), %g6 |
673 | or %g6, %lo(init_thread_union), %g6 | 673 | or %g6, %lo(init_thread_union), %g6 |
674 | ldx [%g6 + TI_TASK], %g4 | 674 | ldx [%g6 + TI_TASK], %g4 |
675 | mov %sp, %l6 | ||
676 | 675 | ||
677 | wr %g0, ASI_P, %asi | 676 | wr %g0, ASI_P, %asi |
678 | mov 1, %g1 | 677 | mov 1, %g1 |
679 | sllx %g1, THREAD_SHIFT, %g1 | 678 | sllx %g1, THREAD_SHIFT, %g1 |
680 | sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1 | 679 | sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1 |
681 | add %g6, %g1, %sp | 680 | add %g6, %g1, %sp |
682 | mov 0, %fp | ||
683 | 681 | ||
684 | /* Set per-cpu pointer initially to zero, this makes | 682 | /* Set per-cpu pointer initially to zero, this makes |
685 | * the boot-cpu use the in-kernel-image per-cpu areas | 683 | * the boot-cpu use the in-kernel-image per-cpu areas |
@@ -706,44 +704,14 @@ tlb_fixup_done: | |||
706 | nop | 704 | nop |
707 | #endif | 705 | #endif |
708 | 706 | ||
709 | mov %l6, %o1 ! OpenPROM stack | ||
710 | call prom_init | 707 | call prom_init |
711 | mov %l7, %o0 ! OpenPROM cif handler | 708 | mov %l7, %o0 ! OpenPROM cif handler |
712 | 709 | ||
713 | /* Initialize current_thread_info()->cpu as early as possible. | 710 | /* To create a one-register-window buffer between the kernel's |
714 | * In order to do that accurately we have to patch up the get_cpuid() | 711 | * initial stack and the last stack frame we use from the firmware, |
715 | * assembler sequences. And that, in turn, requires that we know | 712 | * do the rest of the boot from a C helper function. |
716 | * if we are on a Starfire box or not. While we're here, patch up | ||
717 | * the sun4v sequences as well. | ||
718 | */ | 713 | */ |
719 | call check_if_starfire | 714 | call start_early_boot |
720 | nop | ||
721 | call per_cpu_patch | ||
722 | nop | ||
723 | call sun4v_patch | ||
724 | nop | ||
725 | |||
726 | #ifdef CONFIG_SMP | ||
727 | call hard_smp_processor_id | ||
728 | nop | ||
729 | cmp %o0, NR_CPUS | ||
730 | blu,pt %xcc, 1f | ||
731 | nop | ||
732 | call boot_cpu_id_too_large | ||
733 | nop | ||
734 | /* Not reached... */ | ||
735 | |||
736 | 1: | ||
737 | #else | ||
738 | mov 0, %o0 | ||
739 | #endif | ||
740 | sth %o0, [%g6 + TI_CPU] | ||
741 | |||
742 | call prom_init_report | ||
743 | nop | ||
744 | |||
745 | /* Off we go.... */ | ||
746 | call start_kernel | ||
747 | nop | 715 | nop |
748 | /* Not reached... */ | 716 | /* Not reached... */ |
749 | 717 | ||
diff --git a/arch/sparc/kernel/hvtramp.S b/arch/sparc/kernel/hvtramp.S index b7ddcdd1dea9..cdbfec299f2f 100644 --- a/arch/sparc/kernel/hvtramp.S +++ b/arch/sparc/kernel/hvtramp.S | |||
@@ -109,7 +109,6 @@ hv_cpu_startup: | |||
109 | sllx %g5, THREAD_SHIFT, %g5 | 109 | sllx %g5, THREAD_SHIFT, %g5 |
110 | sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5 | 110 | sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5 |
111 | add %g6, %g5, %sp | 111 | add %g6, %g5, %sp |
112 | mov 0, %fp | ||
113 | 112 | ||
114 | call init_irqwork_curcpu | 113 | call init_irqwork_curcpu |
115 | nop | 114 | nop |
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index e629b8377587..c38d19fc27ba 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/cpu.h> | 30 | #include <linux/cpu.h> |
31 | #include <linux/initrd.h> | 31 | #include <linux/initrd.h> |
32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
33 | #include <linux/start_kernel.h> | ||
33 | 34 | ||
34 | #include <asm/io.h> | 35 | #include <asm/io.h> |
35 | #include <asm/processor.h> | 36 | #include <asm/processor.h> |
@@ -162,7 +163,7 @@ char reboot_command[COMMAND_LINE_SIZE]; | |||
162 | 163 | ||
163 | static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 }; | 164 | static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 }; |
164 | 165 | ||
165 | void __init per_cpu_patch(void) | 166 | static void __init per_cpu_patch(void) |
166 | { | 167 | { |
167 | struct cpuid_patch_entry *p; | 168 | struct cpuid_patch_entry *p; |
168 | unsigned long ver; | 169 | unsigned long ver; |
@@ -254,7 +255,7 @@ void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *start, | |||
254 | } | 255 | } |
255 | } | 256 | } |
256 | 257 | ||
257 | void __init sun4v_patch(void) | 258 | static void __init sun4v_patch(void) |
258 | { | 259 | { |
259 | extern void sun4v_hvapi_init(void); | 260 | extern void sun4v_hvapi_init(void); |
260 | 261 | ||
@@ -323,14 +324,25 @@ static void __init pause_patch(void) | |||
323 | } | 324 | } |
324 | } | 325 | } |
325 | 326 | ||
326 | #ifdef CONFIG_SMP | 327 | void __init start_early_boot(void) |
327 | void __init boot_cpu_id_too_large(int cpu) | ||
328 | { | 328 | { |
329 | prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n", | 329 | int cpu; |
330 | cpu, NR_CPUS); | 330 | |
331 | prom_halt(); | 331 | check_if_starfire(); |
332 | per_cpu_patch(); | ||
333 | sun4v_patch(); | ||
334 | |||
335 | cpu = hard_smp_processor_id(); | ||
336 | if (cpu >= NR_CPUS) { | ||
337 | prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n", | ||
338 | cpu, NR_CPUS); | ||
339 | prom_halt(); | ||
340 | } | ||
341 | current_thread_info()->cpu = cpu; | ||
342 | |||
343 | prom_init_report(); | ||
344 | start_kernel(); | ||
332 | } | 345 | } |
333 | #endif | ||
334 | 346 | ||
335 | /* On Ultra, we support all of the v8 capabilities. */ | 347 | /* On Ultra, we support all of the v8 capabilities. */ |
336 | unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | | 348 | unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | |
diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S index 737f8cbc7d56..88ede1d53b4c 100644 --- a/arch/sparc/kernel/trampoline_64.S +++ b/arch/sparc/kernel/trampoline_64.S | |||
@@ -109,10 +109,13 @@ startup_continue: | |||
109 | brnz,pn %g1, 1b | 109 | brnz,pn %g1, 1b |
110 | nop | 110 | nop |
111 | 111 | ||
112 | sethi %hi(p1275buf), %g2 | 112 | /* Get onto temporary stack which will be in the locked |
113 | or %g2, %lo(p1275buf), %g2 | 113 | * kernel image. |
114 | ldx [%g2 + 0x10], %l2 | 114 | */ |
115 | add %l2, -(192 + 128), %sp | 115 | sethi %hi(tramp_stack), %g1 |
116 | or %g1, %lo(tramp_stack), %g1 | ||
117 | add %g1, TRAMP_STACK_SIZE, %g1 | ||
118 | sub %g1, STACKFRAME_SZ + STACK_BIAS + 256, %sp | ||
116 | flushw | 119 | flushw |
117 | 120 | ||
118 | /* Setup the loop variables: | 121 | /* Setup the loop variables: |
@@ -394,7 +397,6 @@ after_lock_tlb: | |||
394 | sllx %g5, THREAD_SHIFT, %g5 | 397 | sllx %g5, THREAD_SHIFT, %g5 |
395 | sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5 | 398 | sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5 |
396 | add %g6, %g5, %sp | 399 | add %g6, %g5, %sp |
397 | mov 0, %fp | ||
398 | 400 | ||
399 | rdpr %pstate, %o1 | 401 | rdpr %pstate, %o1 |
400 | or %o1, PSTATE_IE, %o1 | 402 | or %o1, PSTATE_IE, %o1 |
diff --git a/arch/sparc/prom/cif.S b/arch/sparc/prom/cif.S index 9c86b4b7d429..8050f381f518 100644 --- a/arch/sparc/prom/cif.S +++ b/arch/sparc/prom/cif.S | |||
@@ -11,11 +11,10 @@ | |||
11 | .text | 11 | .text |
12 | .globl prom_cif_direct | 12 | .globl prom_cif_direct |
13 | prom_cif_direct: | 13 | prom_cif_direct: |
14 | save %sp, -192, %sp | ||
14 | sethi %hi(p1275buf), %o1 | 15 | sethi %hi(p1275buf), %o1 |
15 | or %o1, %lo(p1275buf), %o1 | 16 | or %o1, %lo(p1275buf), %o1 |
16 | ldx [%o1 + 0x0010], %o2 ! prom_cif_stack | 17 | ldx [%o1 + 0x0008], %l2 ! prom_cif_handler |
17 | save %o2, -192, %sp | ||
18 | ldx [%i1 + 0x0008], %l2 ! prom_cif_handler | ||
19 | mov %g4, %l0 | 18 | mov %g4, %l0 |
20 | mov %g5, %l1 | 19 | mov %g5, %l1 |
21 | mov %g6, %l3 | 20 | mov %g6, %l3 |
diff --git a/arch/sparc/prom/init_64.c b/arch/sparc/prom/init_64.c index d95db755828f..110b0d78b864 100644 --- a/arch/sparc/prom/init_64.c +++ b/arch/sparc/prom/init_64.c | |||
@@ -26,13 +26,13 @@ phandle prom_chosen_node; | |||
26 | * It gets passed the pointer to the PROM vector. | 26 | * It gets passed the pointer to the PROM vector. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | extern void prom_cif_init(void *, void *); | 29 | extern void prom_cif_init(void *); |
30 | 30 | ||
31 | void __init prom_init(void *cif_handler, void *cif_stack) | 31 | void __init prom_init(void *cif_handler) |
32 | { | 32 | { |
33 | phandle node; | 33 | phandle node; |
34 | 34 | ||
35 | prom_cif_init(cif_handler, cif_stack); | 35 | prom_cif_init(cif_handler); |
36 | 36 | ||
37 | prom_chosen_node = prom_finddevice(prom_chosen_path); | 37 | prom_chosen_node = prom_finddevice(prom_chosen_path); |
38 | if (!prom_chosen_node || (s32)prom_chosen_node == -1) | 38 | if (!prom_chosen_node || (s32)prom_chosen_node == -1) |
diff --git a/arch/sparc/prom/p1275.c b/arch/sparc/prom/p1275.c index b2340f008ae0..545d8bb79b65 100644 --- a/arch/sparc/prom/p1275.c +++ b/arch/sparc/prom/p1275.c | |||
@@ -20,7 +20,6 @@ | |||
20 | struct { | 20 | struct { |
21 | long prom_callback; /* 0x00 */ | 21 | long prom_callback; /* 0x00 */ |
22 | void (*prom_cif_handler)(long *); /* 0x08 */ | 22 | void (*prom_cif_handler)(long *); /* 0x08 */ |
23 | unsigned long prom_cif_stack; /* 0x10 */ | ||
24 | } p1275buf; | 23 | } p1275buf; |
25 | 24 | ||
26 | extern void prom_world(int); | 25 | extern void prom_world(int); |
@@ -52,5 +51,4 @@ void p1275_cmd_direct(unsigned long *args) | |||
52 | void prom_cif_init(void *cif_handler, void *cif_stack) | 51 | void prom_cif_init(void *cif_handler, void *cif_stack) |
53 | { | 52 | { |
54 | p1275buf.prom_cif_handler = (void (*)(long *))cif_handler; | 53 | p1275buf.prom_cif_handler = (void (*)(long *))cif_handler; |
55 | p1275buf.prom_cif_stack = (unsigned long)cif_stack; | ||
56 | } | 54 | } |