@@ -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