Skip to content

Commit e81ef81

Browse files
davem330gregkh
authored andcommitted
sparc64: Fix register corruption in top-most kernel stack frame during boot.
[ Upstream commit ef3e035 ] 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 raspberrypi#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 <[email protected]> Tested-by: Meelis Roos <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5955d6d commit e81ef81

File tree

10 files changed

+40
-62
lines changed

10 files changed

+40
-62
lines changed

arch/sparc/include/asm/oplib_64.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ struct linux_mem_p1275 {
6262
/* You must call prom_init() before using any of the library services,
6363
* preferably as early as possible. Pass it the romvec pointer.
6464
*/
65-
void prom_init(void *cif_handler, void *cif_stack);
65+
void prom_init(void *cif_handler);
66+
void prom_init_report(void);
6667

6768
/* Boot argument acquisition, returns the boot command line string. */
6869
char *prom_getbootargs(void);

arch/sparc/include/asm/setup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ unsigned long safe_compute_effective_address(struct pt_regs *, unsigned int);
4848
#endif
4949

5050
#ifdef CONFIG_SPARC64
51+
void __init start_early_boot(void);
52+
5153
/* unaligned_64.c */
5254
int handle_ldf_stq(u32 insn, struct pt_regs *regs);
5355
void handle_ld_nf(u32 insn, struct pt_regs *regs);

arch/sparc/kernel/entry.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,10 @@ struct pause_patch_entry {
6565
extern struct pause_patch_entry __pause_3insn_patch,
6666
__pause_3insn_patch_end;
6767

68-
void __init per_cpu_patch(void);
6968
void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *,
7069
struct sun4v_1insn_patch_entry *);
7170
void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *,
7271
struct sun4v_2insn_patch_entry *);
73-
void __init sun4v_patch(void);
74-
void __init boot_cpu_id_too_large(int cpu);
7572
extern unsigned int dcache_parity_tl1_occurred;
7673
extern unsigned int icache_parity_tl1_occurred;
7774

arch/sparc/kernel/head_64.S

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -672,14 +672,12 @@ tlb_fixup_done:
672672
sethi %hi(init_thread_union), %g6
673673
or %g6, %lo(init_thread_union), %g6
674674
ldx [%g6 + TI_TASK], %g4
675-
mov %sp, %l6
676675

677676
wr %g0, ASI_P, %asi
678677
mov 1, %g1
679678
sllx %g1, THREAD_SHIFT, %g1
680679
sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1
681680
add %g6, %g1, %sp
682-
mov 0, %fp
683681

684682
/* Set per-cpu pointer initially to zero, this makes
685683
* the boot-cpu use the in-kernel-image per-cpu areas
@@ -706,44 +704,14 @@ tlb_fixup_done:
706704
nop
707705
#endif
708706

709-
mov %l6, %o1 ! OpenPROM stack
710707
call prom_init
711708
mov %l7, %o0 ! OpenPROM cif handler
712709

713-
/* Initialize current_thread_info()->cpu as early as possible.
714-
* In order to do that accurately we have to patch up the get_cpuid()
715-
* assembler sequences. And that, in turn, requires that we know
716-
* if we are on a Starfire box or not. While we're here, patch up
717-
* the sun4v sequences as well.
710+
/* To create a one-register-window buffer between the kernel's
711+
* initial stack and the last stack frame we use from the firmware,
712+
* do the rest of the boot from a C helper function.
718713
*/
719-
call check_if_starfire
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
714+
call start_early_boot
747715
nop
748716
/* Not reached... */
749717

arch/sparc/kernel/hvtramp.S

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ hv_cpu_startup:
109109
sllx %g5, THREAD_SHIFT, %g5
110110
sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
111111
add %g6, %g5, %sp
112-
mov 0, %fp
113112

114113
call init_irqwork_curcpu
115114
nop

arch/sparc/kernel/setup_64.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/cpu.h>
3131
#include <linux/initrd.h>
3232
#include <linux/module.h>
33+
#include <linux/start_kernel.h>
3334

3435
#include <asm/io.h>
3536
#include <asm/processor.h>
@@ -174,7 +175,7 @@ char reboot_command[COMMAND_LINE_SIZE];
174175

175176
static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 };
176177

177-
void __init per_cpu_patch(void)
178+
static void __init per_cpu_patch(void)
178179
{
179180
struct cpuid_patch_entry *p;
180181
unsigned long ver;
@@ -266,7 +267,7 @@ void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *start,
266267
}
267268
}
268269

269-
void __init sun4v_patch(void)
270+
static void __init sun4v_patch(void)
270271
{
271272
extern void sun4v_hvapi_init(void);
272273

@@ -335,14 +336,25 @@ static void __init pause_patch(void)
335336
}
336337
}
337338

338-
#ifdef CONFIG_SMP
339-
void __init boot_cpu_id_too_large(int cpu)
339+
void __init start_early_boot(void)
340340
{
341-
prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n",
342-
cpu, NR_CPUS);
343-
prom_halt();
341+
int cpu;
342+
343+
check_if_starfire();
344+
per_cpu_patch();
345+
sun4v_patch();
346+
347+
cpu = hard_smp_processor_id();
348+
if (cpu >= NR_CPUS) {
349+
prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n",
350+
cpu, NR_CPUS);
351+
prom_halt();
352+
}
353+
current_thread_info()->cpu = cpu;
354+
355+
prom_init_report();
356+
start_kernel();
344357
}
345-
#endif
346358

347359
/* On Ultra, we support all of the v8 capabilities. */
348360
unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |

arch/sparc/kernel/trampoline_64.S

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,13 @@ startup_continue:
109109
brnz,pn %g1, 1b
110110
nop
111111

112-
sethi %hi(p1275buf), %g2
113-
or %g2, %lo(p1275buf), %g2
114-
ldx [%g2 + 0x10], %l2
115-
add %l2, -(192 + 128), %sp
112+
/* Get onto temporary stack which will be in the locked
113+
* kernel image.
114+
*/
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
116119
flushw
117120

118121
/* Setup the loop variables:
@@ -394,7 +397,6 @@ after_lock_tlb:
394397
sllx %g5, THREAD_SHIFT, %g5
395398
sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
396399
add %g6, %g5, %sp
397-
mov 0, %fp
398400

399401
rdpr %pstate, %o1
400402
or %o1, PSTATE_IE, %o1

arch/sparc/prom/cif.S

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@
1111
.text
1212
.globl prom_cif_direct
1313
prom_cif_direct:
14+
save %sp, -192, %sp
1415
sethi %hi(p1275buf), %o1
1516
or %o1, %lo(p1275buf), %o1
16-
ldx [%o1 + 0x0010], %o2 ! prom_cif_stack
17-
save %o2, -192, %sp
18-
ldx [%i1 + 0x0008], %l2 ! prom_cif_handler
17+
ldx [%o1 + 0x0008], %l2 ! prom_cif_handler
1918
mov %g4, %l0
2019
mov %g5, %l1
2120
mov %g6, %l3

arch/sparc/prom/init_64.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ phandle prom_chosen_node;
2626
* It gets passed the pointer to the PROM vector.
2727
*/
2828

29-
extern void prom_cif_init(void *, void *);
29+
extern void prom_cif_init(void *);
3030

31-
void __init prom_init(void *cif_handler, void *cif_stack)
31+
void __init prom_init(void *cif_handler)
3232
{
3333
phandle node;
3434

35-
prom_cif_init(cif_handler, cif_stack);
35+
prom_cif_init(cif_handler);
3636

3737
prom_chosen_node = prom_finddevice(prom_chosen_path);
3838
if (!prom_chosen_node || (s32)prom_chosen_node == -1)

arch/sparc/prom/p1275.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
struct {
2121
long prom_callback; /* 0x00 */
2222
void (*prom_cif_handler)(long *); /* 0x08 */
23-
unsigned long prom_cif_stack; /* 0x10 */
2423
} p1275buf;
2524

2625
extern void prom_world(int);
@@ -52,5 +51,4 @@ void p1275_cmd_direct(unsigned long *args)
5251
void prom_cif_init(void *cif_handler, void *cif_stack)
5352
{
5453
p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
55-
p1275buf.prom_cif_stack = (unsigned long)cif_stack;
5654
}

0 commit comments

Comments
 (0)