]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/arm/thread-arm.cpp
8dc4cbf8556eb541fcc92da276719e48b90a27e2
[l4.git] / kernel / fiasco / src / kern / arm / thread-arm.cpp
1 INTERFACE [arm]:
2
3 class Trap_state;
4
5 EXTENSION class Thread
6 {
7 public:
8   static void init_per_cpu(unsigned cpu);
9 private:
10   bool _in_exception;
11
12 };
13
14 // ------------------------------------------------------------------------
15 IMPLEMENTATION [arm]:
16
17 #include <cassert>
18 #include <cstdio>
19
20 #include "globals.h"
21 #include "kmem_space.h"
22 #include "mem_op.h"
23 #include "static_assert.h"
24 #include "thread_state.h"
25 #include "types.h"
26
27 enum {
28   FSR_STATUS_MASK = 0x0d,
29   FSR_TRANSL      = 0x05,
30   FSR_DOMAIN      = 0x09,
31   FSR_PERMISSION  = 0x0d,
32 };
33
34 DEFINE_PER_CPU Per_cpu<Thread::Dbg_stack> Thread::dbg_stack;
35
36 PRIVATE static
37 void
38 Thread::print_page_fault_error(Mword e)
39 {
40   char const *const excpts[] =
41     { "reset","undef. insn", "swi", "pref. abort", "data abort",
42       "XXX", "XXX", "XXX" };
43
44   unsigned ex = (e >> 20) & 0x07;
45
46   printf("(%lx) %s, %s(%c)",e & 0xff, excpts[ex],
47          (e & 0x00010000)?"user":"kernel",
48          (e & 0x00020000)?'r':'w');
49 }
50
51 PUBLIC inline
52 void FIASCO_NORETURN
53 Thread::fast_return_to_user(Mword ip, Mword sp, Vcpu_state *arg)
54 {
55   extern char __iret[];
56   Entry_frame *r = regs();
57   assert_kdb((r->psr & Proc::Status_mode_mask) == Proc::Status_mode_user);
58
59   r->ip(ip);
60   r->sp(sp); // user-sp is in lazy user state and thus handled by
61              // fill_user_state()
62   fill_user_state();
63
64   load_tpidruro();
65
66   r->psr &= ~Proc::Status_thumb;
67
68     {
69       register Vcpu_state *r0 asm("r0") = arg;
70
71       asm volatile
72         ("mov sp, %0  \t\n"
73          "mov pc, %1  \t\n"
74          :
75          : "r" (nonull_static_cast<Return_frame*>(r)), "r" (__iret), "r"(r0)
76         );
77     }
78   panic("__builtin_trap()");
79 }
80
81 IMPLEMENT_DEFAULT inline
82 void
83 Thread::init_per_cpu(unsigned)
84 {}
85
86
87 //
88 // Public services
89 //
90
91 IMPLEMENT
92 void
93 Thread::user_invoke()
94 {
95   user_invoke_generic();
96   assert (current()->state() & Thread_ready);
97
98   Trap_state *ts = nonull_static_cast<Trap_state*>
99     (nonull_static_cast<Return_frame*>(current()->regs()));
100
101   static_assert(sizeof(ts->r[0]) == sizeof(Mword), "Size mismatch");
102   Mem::memset_mwords(&ts->r[0], 0, sizeof(ts->r) / sizeof(ts->r[0]));
103
104   if (current()->space()->is_sigma0())
105     ts->r[0] = Kmem_space::kdir()->walk(Kip::k(), 0, false, Ptab::Null_alloc(),
106                                         0).phys(Kip::k());
107
108   extern char __return_from_exception;
109
110   asm volatile
111     ("  mov sp, %[stack_p]    \n"    // set stack pointer to regs structure
112      "  mov pc, %[rfe]        \n"
113      :
114      :
115      [stack_p] "r" (ts),
116      [rfe]     "r" (&__return_from_exception)
117      );
118
119   panic("should never be reached");
120   while (1)
121     {
122       current()->state_del(Thread_ready);
123       current()->schedule();
124     };
125
126   // never returns here
127 }
128
129 IMPLEMENT inline NEEDS["space.h", <cstdio>, "types.h" ,"config.h"]
130 bool Thread::handle_sigma0_page_fault( Address pfa )
131 {
132   return (mem_space()->v_insert(
133         Mem_space::Phys_addr::create((pfa & Config::SUPERPAGE_MASK)),
134         Mem_space::Addr::create(pfa & Config::SUPERPAGE_MASK),
135         Mem_space::Size(Config::SUPERPAGE_SIZE),
136         Mem_space::Page_writable | Mem_space::Page_user_accessible
137         | Mem_space::Page_cacheable)
138       != Mem_space::Insert_err_nomem);
139 }
140
141 PUBLIC static
142 bool
143 Thread::no_copro_handler(Unsigned32, Trap_state *)
144 { return false; }
145
146 typedef bool (*Coproc_insn_handler)(Unsigned32 opcode, Trap_state *ts);
147 static Coproc_insn_handler handle_copro_fault[16] =
148   {
149     Thread::no_copro_handler,
150     Thread::no_copro_handler,
151     Thread::no_copro_handler,
152     Thread::no_copro_handler,
153     Thread::no_copro_handler,
154     Thread::no_copro_handler,
155     Thread::no_copro_handler,
156     Thread::no_copro_handler,
157     Thread::no_copro_handler,
158     Thread::no_copro_handler,
159     Thread::no_copro_handler,
160     Thread::no_copro_handler,
161     Thread::no_copro_handler,
162     Thread::no_copro_handler,
163     Thread::no_copro_handler,
164     Thread::no_copro_handler,
165   };
166
167
168 extern "C" {
169
170   /**
171    * The low-level page fault handler called from entry.S.  We're invoked with
172    * interrupts turned off.  Apart from turning on interrupts in almost
173    * all cases (except for kernel page faults in TCB area), just forwards
174    * the call to Thread::handle_page_fault().
175    * @param pfa page-fault virtual address
176    * @param error_code CPU error code
177    * @return true if page fault could be resolved, false otherwise
178    */
179   Mword pagefault_entry(const Mword pfa, Mword error_code,
180                         const Mword pc, Return_frame *ret_frame)
181   {
182 #if 0 // Double PF detect
183     static unsigned long last_pfa = ~0UL;
184     LOG_MSG_3VAL(current(),"PF", pfa, error_code, pc);
185     if (last_pfa == pfa || pfa == 0)
186       kdb_ke("DBF");
187     last_pfa = pfa;
188 #endif
189     if (EXPECT_FALSE(PF::is_alignment_error(error_code)))
190       {
191         printf("KERNEL%d: alignment error at %08lx (PC: %08lx, SP: %08lx, FSR: %lx, PSR: %lx)\n",
192                current_cpu(), pfa, pc, ret_frame->usp, error_code, ret_frame->psr);
193         return false;
194       }
195
196     Thread *t = current_thread();
197
198     // Pagefault in user mode
199     if (PF::is_usermode_error(error_code))
200       {
201         if (t->vcpu_pagefault(pfa, error_code, pc))
202           return 1;
203         t->state_del(Thread_cancel);
204         Proc::sti();
205       }
206     // or interrupts were enabled
207     else if (!(ret_frame->psr & Proc::Status_IRQ_disabled))
208       Proc::sti();
209
210       // Pagefault in kernel mode and interrupts were disabled
211     else
212       {
213         // page fault in kernel memory region, not present, but mapping exists
214         if (Kmem::is_kmem_page_fault (pfa, error_code))
215           {
216             // We've interrupted a context in the kernel with disabled interrupts,
217             // the page fault address is in the kernel region, the error code is
218             // "not mapped" (as opposed to "access error"), and the region is
219             // actually valid (that is, mapped in Kmem's shared page directory,
220             // just not in the currently active page directory)
221             // Remain cli'd !!!
222           }
223         else if (!Kmem::is_kmem_page_fault (pfa, error_code))
224           {
225             // No error -- just enable interrupts.
226             Proc::sti();
227           }
228         else
229           {
230             // Error: We interrupted a cli'd kernel context touching kernel space
231             if (!Thread::log_page_fault())
232               printf("*P[%lx,%lx,%lx] ", pfa, error_code, pc);
233
234             kdb_ke ("page fault in cli mode");
235           }
236
237       }
238
239     // cache operations we carry out for user space might cause PFs, we just
240     // ignore those
241     if (EXPECT_FALSE(t->is_ignore_mem_op_in_progress()))
242       {
243         ret_frame->pc += 4;
244         return 1;
245       }
246
247     // PFs in the kern_lib_page are always write PFs due to rollbacks and
248     // insn decoding
249     if (EXPECT_FALSE((pc & Kmem::Kern_lib_base) == Kmem::Kern_lib_base))
250       error_code |= (1UL << 11);
251
252     return t->handle_page_fault(pfa, error_code, pc, ret_frame);
253   }
254
255   void slowtrap_entry(Trap_state *ts)
256   {
257     if (0)
258       printf("Trap: pfa=%08lx pc=%08lx err=%08lx psr=%lx\n", ts->pf_address, ts->pc, ts->error_code, ts->psr);
259     Thread *t = current_thread();
260
261     LOG_TRAP;
262
263     if (Config::Support_arm_linux_cache_API)
264       {
265         if (   ts->error_code == 0x00200000
266             && ts->r[7] == 0xf0002)
267           {
268             if (ts->r[2] == 0)
269               Mem_op::arm_mem_cache_maint(Mem_op::Op_cache_coherent,
270                                           (void *)ts->r[0], (void *)ts->r[1]);
271             ts->r[0] = 0;
272             return;
273           }
274       }
275
276     if (ts->exception_is_undef_insn())
277       {
278         Unsigned32 opcode;
279
280         if (ts->psr & Proc::Status_thumb)
281           {
282             Unsigned16 v = *(Unsigned16 *)(ts->pc - 2);
283             if ((v >> 11) <= 0x1c)
284               goto undef_insn;
285
286             opcode = (v << 16) | *(Unsigned16 *)ts->pc;
287           }
288         else
289           opcode = *(Unsigned32 *)(ts->pc - 4);
290
291         if (ts->psr & Proc::Status_thumb)
292           {
293             if (   (opcode & 0xef000000) == 0xef000000 // A6.3.18
294                 || (opcode & 0xff100000) == 0xf9000000)
295               {
296                 if (handle_copro_fault[10](opcode, ts))
297                   return;
298                 goto undef_insn;
299               }
300           }
301         else
302           {
303             if (   (opcode & 0xfe000000) == 0xf2000000 // A5.7.1
304                 || (opcode & 0xff100000) == 0xf4000000)
305               {
306                 if (handle_copro_fault[10](opcode, ts))
307                   return;
308                 goto undef_insn;
309               }
310           }
311
312         if ((opcode & 0x0c000000) == 0x0c000000)
313           {
314             unsigned copro = (opcode >> 8) & 0xf;
315             if (handle_copro_fault[copro](opcode, ts))
316               return;
317           }
318       }
319
320 undef_insn:
321     // send exception IPC if requested
322     if (t->send_exception(ts))
323       return;
324
325     t->halt();
326   }
327
328 };
329
330 IMPLEMENT inline
331 bool
332 Thread::pagein_tcb_request(Return_frame *regs)
333 {
334   //if ((*(Mword*)regs->pc & 0xfff00fff ) == 0xe5900000)
335   if (*(Mword*)regs->pc == 0xe59ee000)
336     {
337       // printf("TCBR: %08lx\n", *(Mword*)regs->pc);
338       // skip faulting instruction
339       regs->pc += 4;
340       // tell program that a pagefault occured we cannot handle
341       regs->psr |= 0x40000000;  // set zero flag in psr
342       regs->km_lr = 0;
343
344       return true;
345     }
346   return false;
347 }
348
349 //---------------------------------------------------------------------------
350 IMPLEMENTATION [arm]:
351
352 #include "trap_state.h"
353
354
355 /** Constructor.
356     @param space the address space
357     @param id user-visible thread ID of the sender
358     @param init_prio initial priority
359     @param mcp thread's maximum controlled priority
360     @post state() != Thread_invalid
361  */
362 IMPLEMENT
363 Thread::Thread()
364   : Sender(0),  // select optimized version of constructor
365     _pager(Thread_ptr::Invalid),
366     _exc_handler(Thread_ptr::Invalid),
367     _del_observer(0)
368 {
369   assert (state(false) == Thread_invalid);
370
371   inc_ref();
372   _space.space(Kernel_task::kernel_task());
373
374   if (Config::Stack_depth)
375     std::memset((char*)this + sizeof(Thread), '5',
376                 Thread::Size-sizeof(Thread)-64);
377
378   // set a magic value -- we use it later to verify the stack hasn't
379   // been overrun
380   _magic = magic;
381   _recover_jmpbuf = 0;
382   _timeout = 0;
383   _in_exception = false;
384
385   *reinterpret_cast<void(**)()> (--_kernel_sp) = user_invoke;
386
387   // clear out user regs that can be returned from the thread_ex_regs
388   // system call to prevent covert channel
389   Entry_frame *r = regs();
390   r->sp(0);
391   r->ip(0);
392   r->psr = Proc::Status_mode_user;
393
394   state_add_dirty(Thread_dead, false);
395
396   // ok, we're ready to go!
397 }
398
399 IMPLEMENT inline
400 Mword
401 Thread::user_sp() const
402 { return regs()->sp(); }
403
404 IMPLEMENT inline
405 void
406 Thread::user_sp(Mword sp)
407 { return regs()->sp(sp); }
408
409 IMPLEMENT inline NEEDS[Thread::exception_triggered]
410 Mword
411 Thread::user_ip() const
412 { return exception_triggered() ? _exc_cont.ip() : regs()->ip(); }
413
414 IMPLEMENT inline
415 Mword
416 Thread::user_flags() const
417 { return 0; }
418
419 IMPLEMENT inline NEEDS[Thread::exception_triggered]
420 void
421 Thread::user_ip(Mword ip)
422 {
423   if (exception_triggered())
424     _exc_cont.ip(ip);
425   else
426     {
427       Entry_frame *r = regs();
428       r->ip(ip);
429       r->psr = (r->psr & ~Proc::Status_mode_mask) | Proc::Status_mode_user;
430     }
431 }
432
433
434 PUBLIC inline NEEDS ["trap_state.h"]
435 int
436 Thread::send_exception_arch(Trap_state *)
437 {
438   // nothing to tweak on ARM
439   return 1;
440 }
441
442 PRIVATE static inline
443 void
444 Thread::save_fpu_state_to_utcb(Trap_state *ts, Utcb *u)
445 {
446   char *esu = (char *)&u->values[21];
447   Fpu::save_user_exception_state(ts, (Fpu::Exception_state_user *)esu);
448 }
449
450 PRIVATE inline
451 bool
452 Thread::invalid_ipc_buffer(void const *a)
453 {
454   if (!_in_exception)
455     return Mem_layout::in_kernel(((Address)a & Config::SUPERPAGE_MASK)
456                                  + Config::SUPERPAGE_SIZE - 1);
457
458   return false;
459 }
460
461 PROTECTED inline
462 int
463 Thread::do_trigger_exception(Entry_frame *r, void *ret_handler)
464 {
465   if (!_exc_cont.valid())
466     {
467       _exc_cont.activate(r, ret_handler);
468       return 1;
469     }
470   return 0;
471 }
472
473
474 PRIVATE static inline NEEDS[Thread::get_ts_tpidruro]
475 bool FIASCO_WARN_RESULT
476 Thread::copy_utcb_to_ts(L4_msg_tag const &tag, Thread *snd, Thread *rcv,
477                         unsigned char rights)
478 {
479   Trap_state *ts = (Trap_state*)rcv->_utcb_handler;
480   Utcb *snd_utcb = snd->utcb().access();
481   Mword       s  = tag.words();
482
483   if (EXPECT_FALSE(rcv->exception_triggered()))
484     {
485       // triggered exception pending
486       Mem::memcpy_mwords (ts, snd_utcb->values, s > 16 ? 16 : s);
487       if (EXPECT_TRUE(s > 20))
488         {
489           // sanitize processor mode
490           // XXX: fix race
491           snd_utcb->values[20] &= ~Proc::Status_mode_mask; // clear mode
492           snd_utcb->values[20] |=   Proc::Status_mode_supervisor
493                                   | Proc::Status_interrupts_disabled;
494
495           Continuation::User_return_frame const *s
496             = reinterpret_cast<Continuation::User_return_frame const *>((char*)&snd_utcb->values[16]);
497
498           rcv->_exc_cont.set(ts, s);
499         }
500     }
501   else
502     {
503       Mem::memcpy_mwords (ts, snd_utcb->values, s > 19 ? 19 : s);
504       if (EXPECT_TRUE(s > 19))
505         ts->pc = snd_utcb->values[19];
506       if (EXPECT_TRUE(s > 20))
507         {
508           // sanitize processor mode
509           Mword p = snd_utcb->values[20];
510           p &= ~(Proc::Status_mode_mask | Proc::Status_interrupts_mask); // clear mode & irqs
511           p |=  Proc::Status_mode_user;
512           ts->psr = p;
513         }
514     }
515
516   if (tag.transfer_fpu() && (rights & L4_fpage::W))
517     snd->transfer_fpu(rcv);
518
519   if ((tag.flags() & 0x8000) && (rights & L4_fpage::W))
520     rcv->utcb().access()->user[2] = snd_utcb->values[25];
521
522   rcv->get_ts_tpidruro(ts);
523
524   bool ret = transfer_msg_items(tag, snd, snd_utcb,
525                                 rcv, rcv->utcb().access(), rights);
526
527   rcv->state_del(Thread_in_exception);
528   return ret;
529 }
530
531
532 PRIVATE static inline NEEDS[Thread::save_fpu_state_to_utcb,
533                             Thread::set_ts_tpidruro]
534 bool FIASCO_WARN_RESULT
535 Thread::copy_ts_to_utcb(L4_msg_tag const &, Thread *snd, Thread *rcv,
536                         unsigned char rights)
537 {
538   Trap_state *ts = (Trap_state*)snd->_utcb_handler;
539
540   {
541     auto guard = lock_guard(cpu_lock);
542     Utcb *rcv_utcb = rcv->utcb().access();
543
544     snd->set_ts_tpidruro(ts);
545
546     Mem::memcpy_mwords(rcv_utcb->values, ts, 16);
547     Continuation::User_return_frame *d
548       = reinterpret_cast<Continuation::User_return_frame *>((char*)&rcv_utcb->values[16]);
549
550     snd->_exc_cont.get(d, ts);
551
552
553     if (EXPECT_TRUE(!snd->exception_triggered()))
554       {
555         rcv_utcb->values[19] = ts->pc;
556         rcv_utcb->values[20] = ts->psr;
557       }
558
559     if (rcv_utcb->inherit_fpu() && (rights & L4_fpage::W))
560       snd->transfer_fpu(rcv);
561
562     save_fpu_state_to_utcb(ts, rcv_utcb);
563   }
564   return true;
565 }
566
567 PROTECTED inline NEEDS[Thread::set_tpidruro]
568 L4_msg_tag
569 Thread::invoke_arch(L4_msg_tag tag, Utcb *utcb)
570 {
571   switch (utcb->values[0] & Opcode_mask)
572     {
573     case Op_set_tpidruro_arm:
574       return set_tpidruro(tag, utcb);
575     default:
576       return commit_result(-L4_err::ENosys);
577     }
578 }
579
580 PROTECTED inline
581 int
582 Thread::sys_control_arch(Utcb *)
583 {
584   return 0;
585 }
586
587 PUBLIC static inline
588 bool
589 Thread::condition_valid(Unsigned32 insn, Unsigned32 psr)
590 {
591   // Matrix of instruction conditions and PSR flags,
592   // index into the table is the condition from insn
593   Unsigned16 v[16] =
594   {
595     0xf0f0,
596     0x0f0f,
597     0xcccc,
598     0x3333,
599     0xff00,
600     0x00ff,
601     0xaaaa,
602     0x5555,
603     0x0c0c,
604     0xf3f3,
605     0xaa55,
606     0x55aa,
607     0x0a05,
608     0xf5fa,
609     0xffff,
610     0xffff
611   };
612
613   return (v[insn >> 28] >> (psr >> 28)) & 1;
614 }
615
616 // ------------------------------------------------------------------------
617 IMPLEMENTATION [arm && armv6plus]:
618
619 PROTECTED inline
620 void
621 Thread::vcpu_resume_user_arch()
622 {
623   // just an experiment for now, we cannot really take the
624   // user-writable register because user-land might already use it
625   asm volatile("mcr p15, 0, %0, c13, c0, 2"
626                : : "r" (utcb().access(true)->values[25]) : "memory");
627 }
628
629 PRIVATE inline
630 L4_msg_tag
631 Thread::set_tpidruro(L4_msg_tag tag, Utcb *utcb)
632 {
633   if (EXPECT_FALSE(tag.words() < 2))
634     return commit_result(-L4_err::EInval);
635
636   _tpidruro = utcb->values[1];
637   if (EXPECT_FALSE(state() & Thread_vcpu_enabled))
638     arch_update_vcpu_state(vcpu_state().access());
639
640   if (this == current_thread())
641     load_tpidruro();
642
643   return commit_result(0);
644 }
645
646 PRIVATE inline
647 void
648 Thread::get_ts_tpidruro(Trap_state *ts)
649 {
650   _tpidruro = ts->tpidruro;
651 }
652
653 PRIVATE inline
654 void
655 Thread::set_ts_tpidruro(Trap_state *ts)
656 {
657   ts->tpidruro = _tpidruro;
658 }
659
660 // ------------------------------------------------------------------------
661 IMPLEMENTATION [arm && !armv6plus]:
662
663 PROTECTED inline
664 void
665 Thread::vcpu_resume_user_arch()
666 {}
667
668 PRIVATE inline
669 L4_msg_tag
670 Thread::set_tpidruro(L4_msg_tag, Utcb *)
671 {
672   return commit_result(-L4_err::EInval);
673 }
674
675 PRIVATE inline
676 void
677 Thread::get_ts_tpidruro(Trap_state *)
678 {}
679
680 PRIVATE inline
681 void
682 Thread::set_ts_tpidruro(Trap_state *)
683 {}
684
685 //-----------------------------------------------------------------------------
686 IMPLEMENTATION [mp]:
687
688 #include "ipi.h"
689 #include "irq_mgr.h"
690
691 EXTENSION class Thread
692 {
693 public:
694   static void kern_kdebug_ipi_entry() asm("kern_kdebug_ipi_entry");
695 };
696
697 class Thread_remote_rq_irq : public Irq_base
698 {
699 public:
700   // we assume IPIs to be top level, no upstream IRQ chips
701   void handle(Upstream_irq const *)
702   { Thread::handle_remote_requests_irq(); }
703
704   Thread_remote_rq_irq()
705   { set_hit(&handler_wrapper<Thread_remote_rq_irq>); }
706
707   void switch_mode(unsigned) {}
708 };
709
710 class Thread_glbl_remote_rq_irq : public Irq_base
711 {
712 public:
713   // we assume IPIs to be top level, no upstream IRQ chips
714   void handle(Upstream_irq const *)
715   { Thread::handle_global_remote_requests_irq(); }
716
717   Thread_glbl_remote_rq_irq()
718   { set_hit(&handler_wrapper<Thread_glbl_remote_rq_irq>); }
719
720   void switch_mode(unsigned) {}
721 };
722
723 class Thread_debug_ipi : public Irq_base
724 {
725 public:
726   // we assume IPIs to be top level, no upstream IRQ chips
727   void handle(Upstream_irq const *)
728   {
729     Ipi::eoi(Ipi::Debug, current_cpu());
730     Thread::kern_kdebug_ipi_entry();
731   }
732
733   Thread_debug_ipi()
734   { set_hit(&handler_wrapper<Thread_debug_ipi>); }
735
736   void switch_mode(unsigned) {}
737 };
738
739 //-----------------------------------------------------------------------------
740 IMPLEMENTATION [mp && !irregular_gic]:
741
742 class Arm_ipis
743 {
744 public:
745   Arm_ipis()
746   {
747     Irq_mgr::mgr->alloc(&remote_rq_ipi, Ipi::Request);
748     Irq_mgr::mgr->alloc(&glbl_remote_rq_ipi, Ipi::Global_request);
749     Irq_mgr::mgr->alloc(&debug_ipi, Ipi::Debug);
750   }
751
752   Thread_remote_rq_irq remote_rq_ipi;
753   Thread_glbl_remote_rq_irq glbl_remote_rq_ipi;
754   Thread_debug_ipi debug_ipi;
755 };
756
757 static Arm_ipis _arm_ipis;
758
759
760 //-----------------------------------------------------------------------------
761 IMPLEMENTATION [arm && fpu]:
762
763 PUBLIC static
764 bool
765 Thread::handle_fpu_trap(Unsigned32 opcode, Trap_state *ts)
766 {
767   if (!condition_valid(opcode, ts->psr))
768     {
769       if (ts->psr & Proc::Status_thumb)
770         ts->pc += 2;
771       return true;
772     }
773
774   if (Fpu::is_enabled())
775     {
776       assert(Fpu::fpu.current().owner() == current());
777       if (Fpu::is_emu_insn(opcode))
778         return Fpu::emulate_insns(opcode, ts);
779     }
780   else if (current_thread()->switchin_fpu())
781     {
782       if (Fpu::is_emu_insn(opcode))
783         return Fpu::emulate_insns(opcode, ts);
784       ts->pc -= (ts->psr & Proc::Status_thumb) ? 2 : 4;
785       return true;
786     }
787
788   ts->error_code |= 0x01000000; // tag fpu undef insn
789   if (Fpu::exc_pending())
790     ts->error_code |= 0x02000000; // fpinst and fpinst2 in utcb will be valid
791
792   return false;
793 }
794
795 PUBLIC static
796 void
797 Thread::init_fpu_trap_handling()
798 {
799   handle_copro_fault[10] = Thread::handle_fpu_trap;
800   handle_copro_fault[11] = Thread::handle_fpu_trap;
801 }
802
803 STATIC_INITIALIZEX(Thread, init_fpu_trap_handling);