Skip to content

Commit cdd8746

Browse files
committed
ARM: vfp: Use undef hook for handling VFP exceptions
Now that the VFP support code has been reimplemented as a C function that takes a struct pt_regs pointer and an opcode, we can use the existing undef_hook framework to deal with undef exceptions triggered by VFP instructions instead of having special handling in assembler. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
1 parent 6ee1e67 commit cdd8746

File tree

4 files changed

+54
-141
lines changed

4 files changed

+54
-141
lines changed

arch/arm/kernel/entry-armv.S

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -557,13 +557,6 @@ ENDPROC(__und_usr)
557557
* co-processor instructions. However, we have to watch out
558558
* for the ARM6/ARM7 SWI bug.
559559
*
560-
* NEON is a special case that has to be handled here. Not all
561-
* NEON instructions are co-processor instructions, so we have
562-
* to make a special case of checking for them. Plus, there's
563-
* five groups of them, so we have a table of mask/opcode pairs
564-
* to check against, and if any match then we branch off into the
565-
* NEON handler code.
566-
*
567560
* Emulators may wish to make use of the following registers:
568561
* r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
569562
* r2 = PC value to resume execution after successful emulation
@@ -575,25 +568,8 @@ ENDPROC(__und_usr)
575568
@
576569
@ Fall-through from Thumb-2 __und_usr
577570
@
578-
#ifdef CONFIG_NEON
579-
get_thread_info r10 @ get current thread
580-
adr r6, .LCneon_thumb_opcodes
581-
b 2f
582-
#endif
583571
call_fpe:
584572
get_thread_info r10 @ get current thread
585-
#ifdef CONFIG_NEON
586-
adr r6, .LCneon_arm_opcodes
587-
2: ldr r5, [r6], #4 @ mask value
588-
ldr r7, [r6], #4 @ opcode bits matching in mask
589-
cmp r5, #0 @ end mask?
590-
beq 1f
591-
and r8, r0, r5
592-
cmp r8, r7 @ NEON instruction?
593-
bne 2b
594-
b do_vfp @ let VFP handler handle this
595-
1:
596-
#endif
597573
tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27
598574
tstne r0, #0x04000000 @ bit 26 set on both ARM and Thumb-2
599575
reteq lr
@@ -620,42 +596,13 @@ call_fpe:
620596
ret.w lr @ CP#7
621597
ret.w lr @ CP#8
622598
ret.w lr @ CP#9
623-
#ifdef CONFIG_VFP
624-
W(b) do_vfp @ CP#10 (VFP)
625-
W(b) do_vfp @ CP#11 (VFP)
626-
#else
627599
ret.w lr @ CP#10 (VFP)
628600
ret.w lr @ CP#11 (VFP)
629-
#endif
630601
ret.w lr @ CP#12
631602
ret.w lr @ CP#13
632603
ret.w lr @ CP#14 (Debug)
633604
ret.w lr @ CP#15 (Control)
634605

635-
#ifdef CONFIG_NEON
636-
.align 6
637-
638-
.LCneon_arm_opcodes:
639-
.word 0xfe000000 @ mask
640-
.word 0xf2000000 @ opcode
641-
642-
.word 0xff100000 @ mask
643-
.word 0xf4000000 @ opcode
644-
645-
.word 0x00000000 @ mask
646-
.word 0x00000000 @ opcode
647-
648-
.LCneon_thumb_opcodes:
649-
.word 0xef000000 @ mask
650-
.word 0xef000000 @ opcode
651-
652-
.word 0xff100000 @ mask
653-
.word 0xf9000000 @ opcode
654-
655-
.word 0x00000000 @ mask
656-
.word 0x00000000 @ opcode
657-
#endif
658-
659606
do_fpe:
660607
add r10, r10, #TI_FPSTATE @ r10 = workspace
661608
ldr_va pc, fp_enter, tmp=r4 @ Call FP module USR entry point

arch/arm/vfp/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
# ccflags-y := -DDEBUG
99
# asflags-y := -DDEBUG
1010

11-
obj-y += vfpmodule.o entry.o vfphw.o vfpsingle.o vfpdouble.o
11+
obj-y += vfpmodule.o vfphw.o vfpsingle.o vfpdouble.o

arch/arm/vfp/entry.S

Lines changed: 0 additions & 31 deletions
This file was deleted.

arch/arm/vfp/vfpmodule.c

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -641,22 +641,47 @@ static int vfp_starting_cpu(unsigned int unused)
641641
return 0;
642642
}
643643

644+
static int vfp_kmode_exception(struct pt_regs *regs, unsigned int instr)
645+
{
646+
/*
647+
* If we reach this point, a floating point exception has been raised
648+
* while running in kernel mode. If the NEON/VFP unit was enabled at the
649+
* time, it means a VFP instruction has been issued that requires
650+
* software assistance to complete, something which is not currently
651+
* supported in kernel mode.
652+
* If the NEON/VFP unit was disabled, and the location pointed to below
653+
* is properly preceded by a call to kernel_neon_begin(), something has
654+
* caused the task to be scheduled out and back in again. In this case,
655+
* rebuilding and running with CONFIG_DEBUG_ATOMIC_SLEEP enabled should
656+
* be helpful in localizing the problem.
657+
*/
658+
if (fmrx(FPEXC) & FPEXC_EN)
659+
pr_crit("BUG: unsupported FP instruction in kernel mode\n");
660+
else
661+
pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n");
662+
pr_crit("FPEXC == 0x%08x\n", fmrx(FPEXC));
663+
return 1;
664+
}
665+
644666
/*
645-
* vfp_support_entry - Handle VFP exception from user mode
667+
* vfp_support_entry - Handle VFP exception
646668
*
647669
* @regs: pt_regs structure holding the register state at exception entry
648670
* @trigger: The opcode of the instruction that triggered the exception
649671
*
650672
* Returns 0 if the exception was handled, or an error code otherwise.
651673
*/
652-
asmlinkage int vfp_support_entry(struct pt_regs *regs, u32 trigger)
674+
static int vfp_support_entry(struct pt_regs *regs, u32 trigger)
653675
{
654676
struct thread_info *ti = current_thread_info();
655677
u32 fpexc;
656678

657679
if (unlikely(!have_vfp))
658680
return -ENODEV;
659681

682+
if (!user_mode(regs))
683+
return vfp_kmode_exception(regs, trigger);
684+
660685
local_bh_disable();
661686
fpexc = fmrx(FPEXC);
662687

@@ -722,7 +747,6 @@ asmlinkage int vfp_support_entry(struct pt_regs *regs, u32 trigger)
722747
* replay the instruction that trapped.
723748
*/
724749
fmxr(FPEXC, fpexc);
725-
regs->ARM_pc -= 4;
726750
} else {
727751
/* Check for synchronous or asynchronous exceptions */
728752
if (!(fpexc & (FPEXC_EX | FPEXC_DEX))) {
@@ -743,78 +767,47 @@ asmlinkage int vfp_support_entry(struct pt_regs *regs, u32 trigger)
743767
fpexc |= FPEXC_DEX;
744768
}
745769
}
746-
bounce: VFP_bounce(trigger, fpexc, regs);
770+
bounce: regs->ARM_pc += 4;
771+
VFP_bounce(trigger, fpexc, regs);
747772
}
748773

749774
local_bh_enable();
750775
return 0;
751776
}
752777

753-
#ifdef CONFIG_KERNEL_MODE_NEON
754-
755-
static int vfp_kmode_exception(struct pt_regs *regs, unsigned int instr)
756-
{
757-
/*
758-
* If we reach this point, a floating point exception has been raised
759-
* while running in kernel mode. If the NEON/VFP unit was enabled at the
760-
* time, it means a VFP instruction has been issued that requires
761-
* software assistance to complete, something which is not currently
762-
* supported in kernel mode.
763-
* If the NEON/VFP unit was disabled, and the location pointed to below
764-
* is properly preceded by a call to kernel_neon_begin(), something has
765-
* caused the task to be scheduled out and back in again. In this case,
766-
* rebuilding and running with CONFIG_DEBUG_ATOMIC_SLEEP enabled should
767-
* be helpful in localizing the problem.
768-
*/
769-
if (fmrx(FPEXC) & FPEXC_EN)
770-
pr_crit("BUG: unsupported FP instruction in kernel mode\n");
771-
else
772-
pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n");
773-
pr_crit("FPEXC == 0x%08x\n", fmrx(FPEXC));
774-
return 1;
775-
}
776-
777-
static struct undef_hook vfp_kmode_exception_hook[] = {{
778+
static struct undef_hook neon_support_hook[] = {{
778779
.instr_mask = 0xfe000000,
779780
.instr_val = 0xf2000000,
780-
.cpsr_mask = MODE_MASK | PSR_T_BIT,
781-
.cpsr_val = SVC_MODE,
782-
.fn = vfp_kmode_exception,
781+
.cpsr_mask = PSR_T_BIT,
782+
.cpsr_val = 0,
783+
.fn = vfp_support_entry,
783784
}, {
784785
.instr_mask = 0xff100000,
785786
.instr_val = 0xf4000000,
786-
.cpsr_mask = MODE_MASK | PSR_T_BIT,
787-
.cpsr_val = SVC_MODE,
788-
.fn = vfp_kmode_exception,
787+
.cpsr_mask = PSR_T_BIT,
788+
.cpsr_val = 0,
789+
.fn = vfp_support_entry,
789790
}, {
790791
.instr_mask = 0xef000000,
791792
.instr_val = 0xef000000,
792-
.cpsr_mask = MODE_MASK | PSR_T_BIT,
793-
.cpsr_val = SVC_MODE | PSR_T_BIT,
794-
.fn = vfp_kmode_exception,
793+
.cpsr_mask = PSR_T_BIT,
794+
.cpsr_val = PSR_T_BIT,
795+
.fn = vfp_support_entry,
795796
}, {
796797
.instr_mask = 0xff100000,
797798
.instr_val = 0xf9000000,
798-
.cpsr_mask = MODE_MASK | PSR_T_BIT,
799-
.cpsr_val = SVC_MODE | PSR_T_BIT,
800-
.fn = vfp_kmode_exception,
801-
}, {
802-
.instr_mask = 0x0c000e00,
803-
.instr_val = 0x0c000a00,
804-
.cpsr_mask = MODE_MASK,
805-
.cpsr_val = SVC_MODE,
806-
.fn = vfp_kmode_exception,
799+
.cpsr_mask = PSR_T_BIT,
800+
.cpsr_val = PSR_T_BIT,
801+
.fn = vfp_support_entry,
807802
}};
808803

809-
static int __init vfp_kmode_exception_hook_init(void)
810-
{
811-
int i;
804+
static struct undef_hook vfp_support_hook = {
805+
.instr_mask = 0x0c000e00,
806+
.instr_val = 0x0c000a00,
807+
.fn = vfp_support_entry,
808+
};
812809

813-
for (i = 0; i < ARRAY_SIZE(vfp_kmode_exception_hook); i++)
814-
register_undef_hook(&vfp_kmode_exception_hook[i]);
815-
return 0;
816-
}
817-
subsys_initcall(vfp_kmode_exception_hook_init);
810+
#ifdef CONFIG_KERNEL_MODE_NEON
818811

819812
/*
820813
* Kernel-side NEON support functions
@@ -919,8 +912,11 @@ static int __init vfp_init(void)
919912
* for NEON if the hardware has the MVFR registers.
920913
*/
921914
if (IS_ENABLED(CONFIG_NEON) &&
922-
(fmrx(MVFR1) & 0x000fff00) == 0x00011100)
915+
(fmrx(MVFR1) & 0x000fff00) == 0x00011100) {
923916
elf_hwcap |= HWCAP_NEON;
917+
for (int i = 0; i < ARRAY_SIZE(neon_support_hook); i++)
918+
register_undef_hook(&neon_support_hook[i]);
919+
}
924920

925921
if (IS_ENABLED(CONFIG_VFPv3)) {
926922
u32 mvfr0 = fmrx(MVFR0);
@@ -989,6 +985,7 @@ static int __init vfp_init(void)
989985

990986
have_vfp = true;
991987

988+
register_undef_hook(&vfp_support_hook);
992989
thread_register_notifier(&vfp_notifier_block);
993990
vfp_pm_init();
994991

0 commit comments

Comments
 (0)