Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 9030/1: entry: omit FP emulation for UND exceptions taken in kernel mode

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.

Cc: Dmitry Osipenko <digetx@gmail.com>
Cc: Kees Cook <keescook@chromium.org>
Fixes: eff8728fe698 ("vmlinux.lds.h: Add PGO and AutoFDO input sections")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

authored by

Ard Biesheuvel and committed by
Russell King
f77ac2e3 3c9f5708

+49 -30
+2 -23
arch/arm/kernel/entry-armv.S
··· 252 252 #else 253 253 svc_entry 254 254 #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 277 255 278 256 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 280 259 mov r0, sp @ struct pt_regs *regs 281 260 bl __und_fault 282 261
-5
arch/arm/vfp/vfphw.S
··· 79 79 DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10 80 80 81 81 .fpu vfpv2 82 - ldr r3, [sp, #S_PSR] @ Neither lazy restore nor FP exceptions 83 - and r3, r3, #MODE_MASK @ are supported in kernel mode 84 - teq r3, #USR_MODE 85 - bne vfp_kmode_exception @ Returns through lr 86 - 87 82 VFPFMRX r1, FPEXC @ Is the VFP enabled? 88 83 DBGSTR1 "fpexc %08x", r1 89 84 tst r1, #FPEXC_EN
+47 -2
arch/arm/vfp/vfpmodule.c
··· 23 23 #include <asm/cputype.h> 24 24 #include <asm/system_info.h> 25 25 #include <asm/thread_notify.h> 26 + #include <asm/traps.h> 26 27 #include <asm/vfp.h> 27 28 28 29 #include "vfpinstr.h" ··· 643 642 return 0; 644 643 } 645 644 646 - void vfp_kmode_exception(void) 645 + #ifdef CONFIG_KERNEL_MODE_NEON 646 + 647 + static int vfp_kmode_exception(struct pt_regs *regs, unsigned int instr) 647 648 { 648 649 /* 649 650 * If we reach this point, a floating point exception has been raised ··· 663 660 pr_crit("BUG: unsupported FP instruction in kernel mode\n"); 664 661 else 665 662 pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n"); 663 + pr_crit("FPEXC == 0x%08x\n", fmrx(FPEXC)); 664 + return 1; 666 665 } 667 666 668 - #ifdef CONFIG_KERNEL_MODE_NEON 667 + static struct undef_hook vfp_kmode_exception_hook[] = {{ 668 + .instr_mask = 0xfe000000, 669 + .instr_val = 0xf2000000, 670 + .cpsr_mask = MODE_MASK | PSR_T_BIT, 671 + .cpsr_val = SVC_MODE, 672 + .fn = vfp_kmode_exception, 673 + }, { 674 + .instr_mask = 0xff100000, 675 + .instr_val = 0xf4000000, 676 + .cpsr_mask = MODE_MASK | PSR_T_BIT, 677 + .cpsr_val = SVC_MODE, 678 + .fn = vfp_kmode_exception, 679 + }, { 680 + .instr_mask = 0xef000000, 681 + .instr_val = 0xef000000, 682 + .cpsr_mask = MODE_MASK | PSR_T_BIT, 683 + .cpsr_val = SVC_MODE | PSR_T_BIT, 684 + .fn = vfp_kmode_exception, 685 + }, { 686 + .instr_mask = 0xff100000, 687 + .instr_val = 0xf9000000, 688 + .cpsr_mask = MODE_MASK | PSR_T_BIT, 689 + .cpsr_val = SVC_MODE | PSR_T_BIT, 690 + .fn = vfp_kmode_exception, 691 + }, { 692 + .instr_mask = 0x0c000e00, 693 + .instr_val = 0x0c000a00, 694 + .cpsr_mask = MODE_MASK, 695 + .cpsr_val = SVC_MODE, 696 + .fn = vfp_kmode_exception, 697 + }}; 698 + 699 + static int __init vfp_kmode_exception_hook_init(void) 700 + { 701 + int i; 702 + 703 + for (i = 0; i < ARRAY_SIZE(vfp_kmode_exception_hook); i++) 704 + register_undef_hook(&vfp_kmode_exception_hook[i]); 705 + return 0; 706 + } 707 + core_initcall(vfp_kmode_exception_hook_init); 669 708 670 709 /* 671 710 * Kernel-side NEON support functions