Skip to content

Commit a47b395

Browse files
ardbiesheuvelgregkh
authored andcommitted
ARM: 9030/1: entry: omit FP emulation for UND exceptions taken in kernel mode
commit f77ac2e378be9dd61eb88728f0840642f045d9d1 upstream. There are a couple of problems with the exception entry code that deals with FP exceptions (which are reported as UND exceptions) when building the kernel in Thumb2 mode: - the conditional branch to vfp_kmode_exception in vfp_support_entry() may be out of range for its target, depending on how the linker decides to arrange the sections; - when the UND exception is taken in kernel mode, the emulation handling logic is entered via the 'call_fpe' label, which means we end up using the wrong value/mask pairs to match and detect the NEON opcodes. Since UND exceptions in kernel mode are unlikely to occur on a hot path (as opposed to the user mode version which is invoked for VFP support code and lazy restore), we can use the existing undef hook machinery for any kernel mode instruction emulation that is needed, including calling the existing vfp_kmode_exception() routine for unexpected cases. So drop the call to call_fpe, and instead, install an undef hook that will get called for NEON and VFP instructions that trigger an UND exception in kernel mode. While at it, make sure that the PC correction is accurate for the execution mode where the exception was taken, by checking the PSR Thumb bit. [nd: fix conflict in arch/arm/vfp/vfphw.S due to missing commit 2cbd1cc3dcd3 ("ARM: 8991/1: use VFP assembler mnemonics if available")] Fixes: eff8728fe698 ("vmlinux.lds.h: Add PGO and AutoFDO input sections") Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Russell King <[email protected]> Signed-off-by: Nick Desaulniers <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Reviewed-by: Nick Desaulniers <[email protected]> Cc: Dmitry Osipenko <[email protected]> Cc: Kees Cook <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 34794bc commit a47b395

File tree

3 files changed

+49
-30
lines changed

3 files changed

+49
-30
lines changed

arch/arm/kernel/entry-armv.S

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -252,31 +252,10 @@ __und_svc:
252252
#else
253253
svc_entry
254254
#endif
255-
@
256-
@ call emulation code, which returns using r9 if it has emulated
257-
@ the instruction, or the more conventional lr if we are to treat
258-
@ this as a real undefined instruction
259-
@
260-
@ r0 - instruction
261-
@
262-
#ifndef CONFIG_THUMB2_KERNEL
263-
ldr r0, [r4, #-4]
264-
#else
265-
mov r1, #2
266-
ldrh r0, [r4, #-2] @ Thumb instruction at LR - 2
267-
cmp r0, #0xe800 @ 32-bit instruction if xx >= 0
268-
blo __und_svc_fault
269-
ldrh r9, [r4] @ bottom 16 bits
270-
add r4, r4, #2
271-
str r4, [sp, #S_PC]
272-
orr r0, r9, r0, lsl #16
273-
#endif
274-
badr r9, __und_svc_finish
275-
mov r2, r4
276-
bl call_fpe
277255

278256
mov r1, #4 @ PC correction to apply
279-
__und_svc_fault:
257+
THUMB( tst r5, #PSR_T_BIT ) @ exception taken in Thumb mode?
258+
THUMB( movne r1, #2 ) @ if so, fix up PC correction
280259
mov r0, sp @ struct pt_regs *regs
281260
bl __und_fault
282261

arch/arm/vfp/vfphw.S

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@
7878
ENTRY(vfp_support_entry)
7979
DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10
8080

81-
ldr r3, [sp, #S_PSR] @ Neither lazy restore nor FP exceptions
82-
and r3, r3, #MODE_MASK @ are supported in kernel mode
83-
teq r3, #USR_MODE
84-
bne vfp_kmode_exception @ Returns through lr
85-
8681
VFPFMRX r1, FPEXC @ Is the VFP enabled?
8782
DBGSTR1 "fpexc %08x", r1
8883
tst r1, #FPEXC_EN

arch/arm/vfp/vfpmodule.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <asm/cputype.h>
2424
#include <asm/system_info.h>
2525
#include <asm/thread_notify.h>
26+
#include <asm/traps.h>
2627
#include <asm/vfp.h>
2728

2829
#include "vfpinstr.h"
@@ -642,7 +643,9 @@ static int vfp_starting_cpu(unsigned int unused)
642643
return 0;
643644
}
644645

645-
void vfp_kmode_exception(void)
646+
#ifdef CONFIG_KERNEL_MODE_NEON
647+
648+
static int vfp_kmode_exception(struct pt_regs *regs, unsigned int instr)
646649
{
647650
/*
648651
* If we reach this point, a floating point exception has been raised
@@ -660,9 +663,51 @@ void vfp_kmode_exception(void)
660663
pr_crit("BUG: unsupported FP instruction in kernel mode\n");
661664
else
662665
pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n");
666+
pr_crit("FPEXC == 0x%08x\n", fmrx(FPEXC));
667+
return 1;
663668
}
664669

665-
#ifdef CONFIG_KERNEL_MODE_NEON
670+
static struct undef_hook vfp_kmode_exception_hook[] = {{
671+
.instr_mask = 0xfe000000,
672+
.instr_val = 0xf2000000,
673+
.cpsr_mask = MODE_MASK | PSR_T_BIT,
674+
.cpsr_val = SVC_MODE,
675+
.fn = vfp_kmode_exception,
676+
}, {
677+
.instr_mask = 0xff100000,
678+
.instr_val = 0xf4000000,
679+
.cpsr_mask = MODE_MASK | PSR_T_BIT,
680+
.cpsr_val = SVC_MODE,
681+
.fn = vfp_kmode_exception,
682+
}, {
683+
.instr_mask = 0xef000000,
684+
.instr_val = 0xef000000,
685+
.cpsr_mask = MODE_MASK | PSR_T_BIT,
686+
.cpsr_val = SVC_MODE | PSR_T_BIT,
687+
.fn = vfp_kmode_exception,
688+
}, {
689+
.instr_mask = 0xff100000,
690+
.instr_val = 0xf9000000,
691+
.cpsr_mask = MODE_MASK | PSR_T_BIT,
692+
.cpsr_val = SVC_MODE | PSR_T_BIT,
693+
.fn = vfp_kmode_exception,
694+
}, {
695+
.instr_mask = 0x0c000e00,
696+
.instr_val = 0x0c000a00,
697+
.cpsr_mask = MODE_MASK,
698+
.cpsr_val = SVC_MODE,
699+
.fn = vfp_kmode_exception,
700+
}};
701+
702+
static int __init vfp_kmode_exception_hook_init(void)
703+
{
704+
int i;
705+
706+
for (i = 0; i < ARRAY_SIZE(vfp_kmode_exception_hook); i++)
707+
register_undef_hook(&vfp_kmode_exception_hook[i]);
708+
return 0;
709+
}
710+
core_initcall(vfp_kmode_exception_hook_init);
666711

667712
/*
668713
* Kernel-side NEON support functions

0 commit comments

Comments
 (0)