Commit 6f6060a5 authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Thomas Gleixner

x86/apm: Don't access __preempt_count with zeroed fs

APM_DO_POP_SEGS does not restore fs/gs which were zeroed by
APM_DO_ZERO_SEGS. Trying to access __preempt_count with
zeroed fs doesn't really work.

Move the ibrs call outside the APM_DO_SAVE_SEGS/APM_DO_RESTORE_SEGS
invocations so that fs is actually restored before calling
preempt_enable().

Fixes the following sort of oopses:
[    0.313581] general protection fault: 0000 [#1] PREEMPT SMP
[    0.313803] Modules linked in:
[    0.314040] CPU: 0 PID: 268 Comm: kapmd Not tainted 4.16.0-rc1-triton-bisect-00090-gdd84441a #19
[    0.316161] EIP: __apm_bios_call_simple+0xc8/0x170
[    0.316161] EFLAGS: 00210016 CPU: 0
[    0.316161] EAX: 00000102 EBX: 00000000 ECX: 00000102 EDX: 00000000
[    0.316161] ESI: 0000530e EDI: dea95f64 EBP: dea95f18 ESP: dea95ef0
[    0.316161]  DS: 007b ES: 007b FS: 0000 GS: 0000 SS: 0068
[    0.316161] CR0: 80050033 CR2: 00000000 CR3: 015d3000 CR4: 000006d0
[    0.316161] Call Trace:
[    0.316161]  ? cpumask_weight.constprop.15+0x20/0x20
[    0.316161]  on_cpu0+0x44/0x70
[    0.316161]  apm+0x54e/0x720
[    0.316161]  ? __switch_to_asm+0x26/0x40
[    0.316161]  ? __schedule+0x17d/0x590
[    0.316161]  kthread+0xc0/0xf0
[    0.316161]  ? proc_apm_show+0x150/0x150
[    0.316161]  ? kthread_create_worker_on_cpu+0x20/0x20
[    0.316161]  ret_from_fork+0x2e/0x38
[    0.316161] Code: da 8e c2 8e e2 8e ea 57 55 2e ff 1d e0 bb 5d b1 0f 92 c3 5d 5f 07 1f 89 47 0c 90 8d b4 26 00 00 00 00 90 8d b4 26 00 00 00 00 90 <64> ff 0d 84 16 5c b1 74 7f 8b 45 dc 8e e0 8b 45 d8 8e e8 8b 45
[    0.316161] EIP: __apm_bios_call_simple+0xc8/0x170 SS:ESP: 0068:dea95ef0
[    0.316161] ---[ end trace 656253db2deaa12c ]---

Fixes: dd84441a ("x86/speculation: Use IBRS if available before calling into firmware")
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Cc:  David Woodhouse <dwmw@amazon.co.uk>
Cc:  "H. Peter Anvin" <hpa@zytor.com>
Cc:  x86@kernel.org
Cc: David Woodhouse <dwmw@amazon.co.uk>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Link: https://lkml.kernel.org/r/20180709133534.5963-1-ville.syrjala@linux.intel.com
parent 2c991e40
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
#ifndef _ASM_X86_MACH_DEFAULT_APM_H #ifndef _ASM_X86_MACH_DEFAULT_APM_H
#define _ASM_X86_MACH_DEFAULT_APM_H #define _ASM_X86_MACH_DEFAULT_APM_H
#include <asm/nospec-branch.h>
#ifdef APM_ZERO_SEGS #ifdef APM_ZERO_SEGS
# define APM_DO_ZERO_SEGS \ # define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \ "pushl %%ds\n\t" \
...@@ -34,7 +32,6 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in, ...@@ -34,7 +32,6 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in,
* N.B. We do NOT need a cld after the BIOS call * N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags. * because we always save and restore the flags.
*/ */
firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS __asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t" "pushl %%edi\n\t"
"pushl %%ebp\n\t" "pushl %%ebp\n\t"
...@@ -47,7 +44,6 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in, ...@@ -47,7 +44,6 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in,
"=S" (*esi) "=S" (*esi)
: "a" (func), "b" (ebx_in), "c" (ecx_in) : "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc"); : "memory", "cc");
firmware_restrict_branch_speculation_end();
} }
static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in, static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in,
...@@ -60,7 +56,6 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in, ...@@ -60,7 +56,6 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in,
* N.B. We do NOT need a cld after the BIOS call * N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags. * because we always save and restore the flags.
*/ */
firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS __asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t" "pushl %%edi\n\t"
"pushl %%ebp\n\t" "pushl %%ebp\n\t"
...@@ -73,7 +68,6 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in, ...@@ -73,7 +68,6 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in,
"=S" (si) "=S" (si)
: "a" (func), "b" (ebx_in), "c" (ecx_in) : "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc"); : "memory", "cc");
firmware_restrict_branch_speculation_end();
return error; return error;
} }
......
...@@ -240,6 +240,7 @@ ...@@ -240,6 +240,7 @@
#include <asm/olpc.h> #include <asm/olpc.h>
#include <asm/paravirt.h> #include <asm/paravirt.h>
#include <asm/reboot.h> #include <asm/reboot.h>
#include <asm/nospec-branch.h>
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
extern int (*console_blank_hook)(int); extern int (*console_blank_hook)(int);
...@@ -614,11 +615,13 @@ static long __apm_bios_call(void *_call) ...@@ -614,11 +615,13 @@ static long __apm_bios_call(void *_call)
gdt[0x40 / 8] = bad_bios_desc; gdt[0x40 / 8] = bad_bios_desc;
apm_irq_save(flags); apm_irq_save(flags);
firmware_restrict_branch_speculation_start();
APM_DO_SAVE_SEGS; APM_DO_SAVE_SEGS;
apm_bios_call_asm(call->func, call->ebx, call->ecx, apm_bios_call_asm(call->func, call->ebx, call->ecx,
&call->eax, &call->ebx, &call->ecx, &call->edx, &call->eax, &call->ebx, &call->ecx, &call->edx,
&call->esi); &call->esi);
APM_DO_RESTORE_SEGS; APM_DO_RESTORE_SEGS;
firmware_restrict_branch_speculation_end();
apm_irq_restore(flags); apm_irq_restore(flags);
gdt[0x40 / 8] = save_desc_40; gdt[0x40 / 8] = save_desc_40;
put_cpu(); put_cpu();
...@@ -690,10 +693,12 @@ static long __apm_bios_call_simple(void *_call) ...@@ -690,10 +693,12 @@ static long __apm_bios_call_simple(void *_call)
gdt[0x40 / 8] = bad_bios_desc; gdt[0x40 / 8] = bad_bios_desc;
apm_irq_save(flags); apm_irq_save(flags);
firmware_restrict_branch_speculation_start();
APM_DO_SAVE_SEGS; APM_DO_SAVE_SEGS;
error = apm_bios_call_simple_asm(call->func, call->ebx, call->ecx, error = apm_bios_call_simple_asm(call->func, call->ebx, call->ecx,
&call->eax); &call->eax);
APM_DO_RESTORE_SEGS; APM_DO_RESTORE_SEGS;
firmware_restrict_branch_speculation_end();
apm_irq_restore(flags); apm_irq_restore(flags);
gdt[0x40 / 8] = save_desc_40; gdt[0x40 / 8] = save_desc_40;
put_cpu(); put_cpu();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment