Module: xenomai-forge
Branch: next
Commit: bcc67a0093e5afe4301eae3ce812fc5270842432
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=bcc67a0093e5afe4301eae3ce812fc5270842432

Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>
Date:   Sat Jan 11 15:52:04 2014 +0100

cobalt/arm: fix VFP related lockup

Running Xenomai compiled with HW FPU support on a processor without VFP
causes a lockup. Remedy that by testing at run-time whether the processor
supports VFP before using VFP instructions.

Use CONFIG_JUMP_LABEL to make the check as lightweight as possible.

---

 kernel/cobalt/arch/arm/switch.S |   16 ++++++++++++++++
 kernel/cobalt/arch/arm/thread.c |   30 ++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/kernel/cobalt/arch/arm/switch.S b/kernel/cobalt/arch/arm/switch.S
index 969b28d..7048695 100644
--- a/kernel/cobalt/arch/arm/switch.S
+++ b/kernel/cobalt/arch/arm/switch.S
@@ -49,6 +49,19 @@
 
        .macro fpu_switch tmp
 #ifdef CONFIG_VFP
+#if __LINUX_ARM_ARCH__ <= 6
+#ifdef CONFIG_JUMP_LABEL
+9998:  nop
+       .pushsection __jump_table, "aw"
+       .word   9998b, 9999f, __xeno_vfp_key
+       .popsection
+#else
+       ldr     \tmp, =elf_hwcap
+       ldr     \tmp, [\tmp]
+       tst     \tmp, #HWCAP_VFP
+       beq     9999f
+#endif
+#endif
        @ Always disable VFP so we can lazily save/restore the old
        @ state. This occurs in the context of the previous thread.
        VFPFMRX \tmp, FPEXC
@@ -58,6 +71,9 @@
        bic     \tmp, \tmp, #FPEXC_EN
 #endif
        VFPFMXR FPEXC, \tmp
+#if __LINUX_ARM_ARCH__ <= 6
+9999:
+#endif
 #endif
        .endm
 
diff --git a/kernel/cobalt/arch/arm/thread.c b/kernel/cobalt/arch/arm/thread.c
index 53c9879..f5c7528 100644
--- a/kernel/cobalt/arch/arm/thread.c
+++ b/kernel/cobalt/arch/arm/thread.c
@@ -23,9 +23,14 @@
 #include <linux/sched.h>
 #include <linux/ipipe.h>
 #include <linux/mm.h>
+#include <linux/jump_label.h>
 #include <asm/mmu_context.h>
 #include <cobalt/kernel/thread.h>
 
+static unsigned vfp_checked;
+static DEFINE_MUTEX(vfp_check_lock);
+struct static_key __xeno_vfp_key = STATIC_KEY_INIT_TRUE;
+
 asmlinkage void __asm_thread_switch(struct thread_info *out,
                                    struct thread_info *in);
 
@@ -58,7 +63,14 @@ static inline union vfp_state *get_fpu_owner(void)
        unsigned int cpu;
 #ifdef CONFIG_SMP
        unsigned int fpexc;
+#endif
 
+#if __LINUX_ARM_ARCH__ <= 6
+       if (!static_key_true(&__xeno_vfp_key))
+               return NULL;
+#endif
+
+#ifdef CONFIG_SMP
        fpexc = do_vfp_fmrx(FPEXC);
        if (!(fpexc & FPEXC_EN))
                return NULL;
@@ -260,6 +272,13 @@ int xnarch_handle_fpu_fault(struct xnthread *from,
                /* FPU is already enabled, probably an exception */
                return 0;
 
+#if __LINUX_ARM_ARCH__ <= 6
+       if (!static_key_true(&__xeno_vfp_key))
+               /* VFP instruction emitted, on a cpu without VFP, this
+                  is an error */
+               return 0;
+#endif
+
        xnthread_set_state(to, XNFPU);
        xnarch_switch_fpu(from, to);
 
@@ -271,8 +290,19 @@ int xnarch_handle_fpu_fault(struct xnthread *from,
 void xnarch_init_shadow_tcb(struct xnthread *thread)
 {
        struct xnarchtcb *tcb = xnthread_archtcb(thread);
+
        tcb->fpup = &task_thread_info(tcb->core.host_task)->vfpstate;
 
+       if (vfp_checked == 0) {
+               mutex_lock(&vfp_check_lock);
+               if (vfp_checked == 0) {
+                       if ((elf_hwcap & HWCAP_VFP) == 0)
+                               static_key_slow_dec(&__xeno_vfp_key);
+                       vfp_checked = 1;
+               }
+               mutex_unlock(&vfp_check_lock);
+       }
+
        /* XNFPU is set upon first FPU fault */
        xnthread_clear_state(thread, XNFPU);
 }


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to